Commit 4f9c9341 authored by Michael Weinrich's avatar Michael Weinrich
Browse files

modules renamed, classes divided in parts for client and server devices

parent fea684ce
...@@ -8,27 +8,32 @@ import louie ...@@ -8,27 +8,32 @@ import louie
from twisted.internet import reactor, defer from twisted.internet import reactor, defer
from coherence.base import Coherence from coherence.base import Coherence
#from coherence.upnp.core.devices.control_point import ControlPoint
from coherence import log from coherence import log
#from coherence.extern.logger import Logger #from coherence.extern.logger import Logger
#log = Logger('UPnT') #log = Logger('UPnT')
from upntest.testhost import TestHost from upntest.host import Host
"""The main class of the whole test suite""" """The main class of the whole test suite"""
class UPnT(log.Loggable): class UPnT(log.Loggable):
logCategory = 'UPnT' logCategory = 'UPnT'
known_hosts = {}
runningDeferreds = []
shutdown_signal = None
def __init__(self, config={}): def __init__(self, config={}):
log.init() log.init()
self.known_hosts = {}
self.runningDeferreds = []
self.shutdown_signal = None
self._hosts_to_remove = []
self._config = config self._config = config
self._coherence = Coherence(self._config); self._coherence = Coherence(self._config);
#if config.get('test_control_point', False):
#self._control_point = ControlPoint(self._coherence)
self.info('connecting signals...') self.info('connecting signals...')
louie.connect(self.datagramReceived, 'UPnT.ssdp_datagram_received', louie.Any, weak=False) louie.connect(self.datagramReceived, 'UPnT.ssdp_datagram_received', louie.Any, weak=False)
louie.connect(self.datagramReceived, 'UPnT.msearch_datagram_received', louie.Any, weak=False) louie.connect(self.datagramReceived, 'UPnT.msearch_datagram_received', louie.Any, weak=False)
...@@ -42,24 +47,30 @@ class UPnT(log.Loggable): ...@@ -42,24 +47,30 @@ class UPnT(log.Loggable):
reactor.addSystemEventTrigger('before', 'shutdown', self.waitForShutdown) reactor.addSystemEventTrigger('before', 'shutdown', self.waitForShutdown)
def datagramReceived(self, data, ip, port): def datagramReceived(self, data, ip, port):
"""debug output for received datagrams""" """
#print '\nDatagram received from %s:%s' % (ip, port) Check if host exists in host list. If not create it and call the
#print 'Datagram data:\n%s' % data validation method.
"""
if not self.known_hosts.has_key(ip): if not self.known_hosts.has_key(ip):
self.known_hosts[ip] = TestHost(ip, self._config) self.known_hosts[ip] = Host(ip, self._config)
self.info('Host added', ip) self.info('Host added', ip)
self.debug('Calling dispatchDiscoveryPacketData') self.debug('Calling dispatchDiscoveryPacketData')
try: #try:
self.known_hosts[ip].dispatchDiscoveryPacketData(data) self.known_hosts[ip].dispatchDiscoveryPacketData(data)
except Exception: #except Exception, error:
pass # self.warning(error)
# GUI update signal # GUI update signal
louie.send('UPnT.host_discovery_end', None, ip) louie.send('UPnT.host_discovery_end', None, ip)
def hostRemoved(self, ip): def hostRemoved(self, ip):
"""Remove host from the known hosts list""" """
self.known_hosts.pop(ip) Remove host from the known hosts list
"""
self.debug('hostRemoved(%r)' % ip)
self._hosts_to_remove.append(ip)
def removeHosts(self): def removeHosts(self):
self.known_hosts = None self.known_hosts = None
......
This diff is collapsed.
...@@ -12,18 +12,16 @@ from twisted.internet import reactor, defer ...@@ -12,18 +12,16 @@ from twisted.internet import reactor, defer
import louie import louie
from upntest.testdevice import TestDevice from upntest.serverdevice import ServerDevice
from upntest.testservice import TestService from upntest.service import Service
from coherence.upnp.core import utils from coherence.upnp.core import utils
from coherence import log from coherence import log
#from coherence.extern.logger import Logger
#log = Logger('UPnT.TestHost')
class TestHost(log.Loggable): class Host(log.Loggable):
"""helper class to keep track of hosts and their behaviour""" """helper class to keep track of hosts and their behaviour"""
logCategory = 'UPnT_TestHost' logCategory = 'UPnT_Host'
def __init__(self, ip, config): def __init__(self, ip, config):
...@@ -33,57 +31,40 @@ class TestHost(log.Loggable): ...@@ -33,57 +31,40 @@ class TestHost(log.Loggable):
self._devices = {} self._devices = {}
self._services = {} self._services = {}
self._create_dev_tree_func = None self._create_dev_tree_func = None
#overall behaviour indicators
self._discovery_packets_valid = True
self._last_announcement = 0
#hints for compliance to recommended behaviour
self._ssdp_max_age_to_low = False
self._mx_value_to_high = False
self._date_header_not_sent = False
def getIp(self): def getIp(self):
return self._ip return self._ip
ip = property(getIp) ip = property(getIp)
def dispatchDiscoveryPacketData(self, data): def dispatchDiscoveryPacketData(self, data):
self.debug('dispatchDiscoveryPacketData')
[header, payload] = data.split('\r\n\r\n', 1) [header, payload] = data.split('\r\n\r\n', 1)
if data.lower().startswith('m-search'): if data.lower().startswith('m-search'):
self.info('Packet is of type M_SEARCH') self.debug('Packet is of type M_SEARCH')
d = self.checkDiscoveryPacket(header, payload) d = self.checkDiscoveryPacket(header, payload)
if d is not None: if d is not None:
#d.addCallback(self.updateDeviceTree, header, d) #d.addCallback(self.updateDeviceTree, header, d)
#d.addErrback(self.showPacketData, header, payload, '', d) #d.addErrback(self.showPacketData, header, payload, '', d)
louie.send('UPnT.ending_deferred', None, d) louie.send('UPnT.ending_deferred', None, d)
else: else:
self._discovery_packets_valid = False
self.showPacketData(None, header, payload, '', None) self.showPacketData(None, header, payload, '', None)
elif data.lower().startswith('notify'): elif data.lower().startswith('notify'):
self.info('Packet is of type NOTIFY') self.debug('Packet is of type NOTIFY')
d = self.checkNotificationPacket(header, payload) d = self.checkNotificationPacket(header, payload)
if d is not None: if d is not None:
d.addCallback(self.updateDeviceTree, header, d) d.addCallback(self.updateDeviceTree, header, d)
d.addErrback(self.showPacketData, header, payload, 'location', d) d.addErrback(self.showPacketData, header, payload, 'location', d)
else: else:
self._discovery_packets_valid = False
self.showPacketData(None, header, payload, '', None) self.showPacketData(None, header, payload, '', None)
elif data.lower().startswith('http'): elif data.lower().startswith('http'):
self.info('Packet is of type HTTP') self.debug('Packet is of type HTTP (M-SEARCH response)')
d = self.checkDiscoveryAnswerPacket(header, payload) d = self.checkDiscoveryAnswerPacket(header, payload)
if d is not None: if d is not None:
d.addCallback(self.updateDeviceTree, header, d) d.addCallback(self.updateDeviceTree, header, d)
d.addErrback(self.showPacketData, header, payload, 'location', d) d.addErrback(self.showPacketData, header, payload, 'location', d)
else: else:
self._discovery_packets_valid = False
self.showPacketData(None, header, payload, '', None) self.showPacketData(None, header, payload, '', None)
def ssdpDatagramsReceived(self):
return (self._ssdp_datagram_num > 0)
def msearchDatagramsReceived(self):
return (self._msearch_datagram_num > 0)
def formatHeader(self, header): def formatHeader(self, header):
"""parses the header and returns a dictionary containing the command """parses the header and returns a dictionary containing the command
and the header lines also as dictionary""" and the header lines also as dictionary"""
...@@ -125,7 +106,7 @@ class TestHost(log.Loggable): ...@@ -125,7 +106,7 @@ class TestHost(log.Loggable):
def updateDeviceTree(self, result, header, d): def updateDeviceTree(self, result, header, d):
"""Register a new device or delete an existing device based on the UUID """Register a new device or delete an existing device based on the UUID
and packet header""" and packet header"""
command, headers = self.formatHeader(header) command, headers = self.formatHeader(header)
usn_split = headers['usn'].split('::') usn_split = headers['usn'].split('::')
...@@ -144,6 +125,7 @@ class TestHost(log.Loggable): ...@@ -144,6 +125,7 @@ class TestHost(log.Loggable):
if headers.has_key('nts'): if headers.has_key('nts'):
action = headers['nts'] action = headers['nts']
else: else:
# packet was an M-SEARCH response therefore it has no NTS header
action = 'ssdp:alive' action = 'ssdp:alive'
if action == 'ssdp:alive': if action == 'ssdp:alive':
...@@ -153,13 +135,13 @@ class TestHost(log.Loggable): ...@@ -153,13 +135,13 @@ class TestHost(log.Loggable):
if self._services.has_key(headers['usn']): if self._services.has_key(headers['usn']):
self._services[headers['usn']].update(headers) self._services[headers['usn']].update(headers)
else: else:
self._services[headers['usn']] = TestService(headers, self._config) self._services[headers['usn']] = Service(headers, self._config)
else: else:
if self._devices.has_key(uuid): if self._devices.has_key(uuid):
self._devices[uuid].update(headers) self._devices[uuid].update(headers)
else: else:
self._devices[uuid] = TestDevice(headers, self._config) self._devices[uuid] = ServerDevice(headers, self._config)
self._devices[uuid].host = self self._devices[uuid].host = self
if self._create_dev_tree_func is None: if self._create_dev_tree_func is None:
...@@ -169,11 +151,14 @@ class TestHost(log.Loggable): ...@@ -169,11 +151,14 @@ class TestHost(log.Loggable):
elif action == 'ssdp:byebye': elif action == 'ssdp:byebye':
if self._devices.has_key(uuid): if upnp_type == 'device':
if upnp_type == 'device': if self._devices.has_key(uuid):
self._devices[uuid].removeDevice(headers) del self._devices[uuid]
else: else:
if self._devices.has_key(uuid):
self._devices[uuid].removeService(headers) self._devices[uuid].removeService(headers)
if self._services.has_key(headers['usn']):
del self._services[headers['usn']]
else: else:
louie.send('UPnT.infoMessage', None, 'Wrong action. Something went really wrong here.') louie.send('UPnT.infoMessage', None, 'Wrong action. Something went really wrong here.')
...@@ -264,10 +249,12 @@ class TestHost(log.Loggable): ...@@ -264,10 +249,12 @@ class TestHost(log.Loggable):
#check if 2nd part of USN header is equal to NT header if NT header is not the UUID #check if 2nd part of USN header is equal to NT header if NT header is not the UUID
if headers['nt'].startswith('uuid:'): if headers['nt'].startswith('uuid:'):
if headers['nt'] != headers['usn']: if headers['nt'] != headers['usn']:
self.debug('NT header doesn\'t match USN header')
return None return None
else: else:
usn_split = headers['usn'].split('::') usn_split = headers['usn'].split('::')
if headers['nt'] != usn_split[1]: if headers['nt'] != usn_split[1]:
self.debug('NT header doesn\'t correspond USN header')
return None return None
#SSDP only needs the header data #SSDP only needs the header data
...@@ -275,11 +262,16 @@ class TestHost(log.Loggable): ...@@ -275,11 +262,16 @@ class TestHost(log.Loggable):
louie.send('UPnT.hostValidationError', None, 'Packet has payload', self.ip) louie.send('UPnT.hostValidationError', None, 'Packet has payload', self.ip)
self.debug('Packet has payload') self.debug('Packet has payload')
return None return None
#NTS has to be ssdp:alive or ssdp:byebye #NTS has to be ssdp:alive or ssdp:byebye
if headers['nts'] == 'ssdp:alive': if headers['nts'] == 'ssdp:alive':
return self.checkAlivePacket(headers) return self.checkAlivePacket(headers)
elif not headers['nts'] == 'ssdp:byebye': elif headers['nts'] == 'ssdp:byebye':
self.debug('ssdp:byebye received')
d = defer.succeed(None)
louie.send('UPnT.running_deferred', None, d)
return d
else:
louie.send('UPnT.hostValidationError', None, 'Wrong NTS header', self.ip) louie.send('UPnT.hostValidationError', None, 'Wrong NTS header', self.ip)
self.debug('Wrong NTS header') self.debug('Wrong NTS header')
return None return None
...@@ -335,8 +327,10 @@ class TestHost(log.Loggable): ...@@ -335,8 +327,10 @@ class TestHost(log.Loggable):
return d return d
def checkDiscoveryPacket(self, header, payload): def checkDiscoveryPacket(self, header, payload):
"""checks content of a discovery packet for errors and returns """
True if everything looks ok""" Check content of a discovery packet for errors and return True if
everything looks ok.
"""
self.info('Processing M-SEARCH request from %s' % self.ip) self.info('Processing M-SEARCH request from %s' % self.ip)
......
...@@ -10,17 +10,16 @@ from StringIO import StringIO ...@@ -10,17 +10,16 @@ from StringIO import StringIO
import louie import louie
from testservice import *
from coherence.upnp.core import utils from coherence.upnp.core import utils
from coherence import log from coherence import log
#from coherence.extern.logger import Logger
#log = Logger('UPnT.TestDevice')
class TestDevice(log.Loggable): class ServerDevice(log.Loggable):
"""Class for storing data about active devices""" """
Class for storing data about server devices like MediaServers or
MediaRenderers.
"""
logCategory = 'UPnT_TestDevice' logCategory = 'UPnT_ServerDevice'
def __init__(self, headers, config): def __init__(self, headers, config):
...@@ -150,18 +149,18 @@ class TestDevice(log.Loggable): ...@@ -150,18 +149,18 @@ class TestDevice(log.Loggable):
self.warning(error) self.warning(error)
louie.send('UPnT.infoMessage', None, 'Device-Description not valid! (UUID %s)' % self._uuid) louie.send('UPnT.infoMessage', None, 'Device-Description not valid! (UUID %s)' % self._uuid)
self.info('Description:\n%s' % description) self.info('Description:\n%s' % description)
return None raise InvalidDescriptionException(self._uuid)
return doc return doc
def validateDescriptionFailed(self, failure, url): def validateDescriptionFailed(self, failure, url):
"""Error while receiving description""" """Error while receiving description"""
self.warning("error requesting", url) self.warning("validateDescriptionFailed %r" % url)
return failure return failure
def checkDeviceAnnouncement(self, doc, device_list, xpath_exp='/upnp:root/upnp:device'): def checkDeviceAnnouncement(self, doc, device_list, xpath_exp='/upnp:root/upnp:device'):
"""Take the description, extract devices and look for a TestDevice object """Take the description, extract devices and look for a ServerDevice object
in the existing devices list. If found, add it to the tree. If not, the in the existing devices list. If found, add it to the tree. If not, the
device wasn't announced.""" device wasn't announced."""
self.debug('checkDeviceAnnouncement') self.debug('checkDeviceAnnouncement')
...@@ -193,17 +192,12 @@ class TestDevice(log.Loggable): ...@@ -193,17 +192,12 @@ class TestDevice(log.Loggable):
return failure return failure
def checkServiceAnnouncement(self, doc, device_list, service_list, xpath_exp='/upnp:root/upnp:device', d=None): def checkServiceAnnouncement(self, doc, device_list, service_list, xpath_exp='/upnp:root/upnp:device', d=None):
"""Take the description, extract services and look for a TestService """Take the description, extract services and look for a Service
object in the existing services list. If found, add it to the tree. If object in the existing services list. If found, add it to the tree. If
not, the service wasn't announced.""" not, the service wasn't announced."""
self.debug('checkServiceAnnouncement') self.debug('checkServiceAnnouncement')
# some error happened in previous callbacks
if doc == None:
return None
ns = {'upnp': 'urn:schemas-upnp-org:device-1-0'} ns = {'upnp': 'urn:schemas-upnp-org:device-1-0'}
self.debug(etree.tostring(xpath_exp, pretty_print=True))
devices = doc.xpath(xpath_exp, ns) devices = doc.xpath(xpath_exp, ns)
for device in devices: for device in devices:
...@@ -284,4 +278,20 @@ class TestDevice(log.Loggable): ...@@ -284,4 +278,20 @@ class TestDevice(log.Loggable):
self._expiration_notification_func = None self._expiration_notification_func = None
louie.send('UPnT.infoMessage', None, 'Device announcement of %s:%s has expired!' % (self._device_type, self._device_version)) louie.send('UPnT.infoMessage', None, 'Device announcement of %s:%s has expired!' % (self._device_type, self._device_version))
\ No newline at end of file def removeService(self, headers):
if self._services.has_key(headers['usn']):
self.debug('Sending UPnT.serviceByeBye...')
louie.send('UPnT.serviceByeBye', None, self._services[headers['usn']])
self._services[headers['usn']].eventchecker.removeEventSeq()
del self._services[headers['usn']]
class InvalidDescriptionException(StandardError):
"""Error representing an invalid device description"""
def __init__(self, uuid):
self.uuid = uuid
def __str__(self):
return 'Device-Description not valid! (UUID %s)' % (self.uuid)
\ No newline at end of file
...@@ -19,13 +19,13 @@ from StringIO import StringIO ...@@ -19,13 +19,13 @@ from StringIO import StringIO
from coherence.upnp.core import utils from coherence.upnp.core import utils
from coherence import log from coherence import log
#from coherence.extern.logger import Logger
#log = Logger('UPnT.TestService')
class TestService(log.Loggable): from upntest.eventing import ServerEventMessageChecks
class Service(log.Loggable):
"""Class for storing data about active devices""" """Class for storing data about active devices"""
logCategory = 'UPnT_TestService' logCategory = 'UPnT_Service'
def __init__(self, headers, config): def __init__(self, headers, config):
...@@ -46,6 +46,8 @@ class TestService(log.Loggable): ...@@ -46,6 +46,8 @@ class TestService(log.Loggable):
self.templates_dir = config.get('templates_dir', None) self.templates_dir = config.get('templates_dir', None)
self.update(headers, True) self.update(headers, True)
self.eventchecker = ServerEventMessageChecks(self)
def __str__(self): def __str__(self):
return self.parentDevice.UUID + '::' + self.serviceType return self.parentDevice.UUID + '::' + self.serviceType
...@@ -60,14 +62,23 @@ class TestService(log.Loggable): ...@@ -60,14 +62,23 @@ class TestService(log.Loggable):
self._parentDevice = parentDevice self._parentDevice = parentDevice
parentDevice = property(getParentDevice, setParentDevice) parentDevice = property(getParentDevice, setParentDevice)
def getEventSubUrl(self):
return self._parentDevice
def setEventSubUrl(self, eventSubURL):
self._eventSubURL = eventSubURL
eventSubUrl = property(getEventSubUrl, setEventSubUrl)
def setScpdUrl(self, scpdUrl): def setScpdUrl(self, scpdUrl):
self._SCPDURL = scpdUrl self._SCPDURL = scpdUrl
def getScpdUrl(self):
return self._SCPDURL
scpdUrl = property(getScpdUrl, setScpdUrl)
def getControlUrl(self):
return self._controlURL
def setControlUrl(self, controlUrl): def setControlUrl(self, controlUrl):
self._controlURL = controlUrl self._controlURL = controlUrl
controlUrl = property(getControlUrl, setControlUrl)
def setEventSubUrl(self, eventSubURL):
self._eventSubURL = eventSubURL
def checkDescriptions(self): def checkDescriptions(self):
"""Check if description of service is valid""" """Check if description of service is valid"""
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment