00001
00002
00003
00004 '''rtctree
00005
00006 Copyright (C) 2009-2014
00007 Geoffrey Biggs
00008 RT-Synthesis Research Group
00009 Intelligent Systems Research Institute,
00010 National Institute of Advanced Industrial Science and Technology (AIST),
00011 Japan
00012 All rights reserved.
00013 Licensed under the Eclipse Public License -v 1.0 (EPL)
00014 http://www.opensource.org/licenses/eclipse-1.0.txt
00015
00016 Objects and functions used to build and store a tree representing a hierarchy
00017 of name servers, directories, managers and components.
00018
00019 '''
00020
00021 from copy import deepcopy
00022 from omniORB import CORBA
00023 import os
00024 import sys
00025
00026 from rtctree import NAMESERVERS_ENV_VAR, ORB_ARGS_ENV_VAR
00027 from rtctree.exceptions import *
00028 from rtctree.path import BadPathError
00029 from rtctree.node import TreeNode
00030 from rtctree.directory import Directory
00031 from rtctree.nameserver import NameServer
00032 from rtctree.manager import Manager
00033 from rtctree.component import Component
00034 from rtctree.utils import filtered, trim_filter
00035
00036
00037
00038
00039
00040 class RTCTree(object):
00041 '''Represents a tree of name servers, directories, managers and components.
00042
00043 This stores the root node. All other nodes branch off from that.
00044
00045 When creating a tree, you may pass no arguments, or a list of name servers
00046 to load, or a path or list of paths (as returned by
00047 rtctree.path.parse_path). If no arguments are given, the tree will load
00048 name servers from the environment variable specified in
00049 NAMESERVERS_ENV_VAR. If a list of servers are given, only those servers
00050 will be loaded.
00051
00052 If paths are given, and the path is just '/', behaviour is as if no path
00053 argument were given. If a path starts with '/' and contains an element
00054 after it, that element will be treated as a name server. Otherwise the path
00055 is considered to be bad.
00056
00057 '''
00058 def __init__(self, servers=None, paths=None, orb=None, filter=[],
00059 dynamic=False, *args, **kwargs):
00060 '''Constructor.
00061
00062 @param servers A list of servers to parse into the tree.
00063 @param paths A list of paths from which to get servers to parse
00064 into the tree.
00065 @param orb If not None, the specified ORB will be used. If None,
00066 the tree object will create its own ORB.
00067 @param filter A list of paths (each a list of strings).
00068 If not empty, then only objects in the paths will
00069 be parsed, to increase speed. If the tail of a
00070 path is a directory, that entire directory will be
00071 parsed. Directories that are not the tail will
00072 only have the next entry in the path parsed.
00073 @param dynamic Use observers to keep the tree up-to-date. For example,
00074 when a component changes state, an observer can notify
00075 RTCTree so that the corresponding object in the tree can
00076 be updated. Currently this only affects components.
00077 @raises NonRootPathError
00078
00079 '''
00080 super(RTCTree, self).__init__()
00081 self._root = TreeNode('/', None, dynamic=dynamic)
00082 self._create_orb(orb)
00083 self._dynamic = dynamic
00084 if servers:
00085 self._parse_name_servers(servers, filter=filter, dynamic=dynamic)
00086 if paths:
00087 if type(paths[0]) == str:
00088 if paths[0][0] != '/':
00089 raise NonRootPathError(paths[0])
00090 if len(paths) > 1:
00091 self.add_name_server(paths[1], filter=filter,
00092 dynamic=dynamic)
00093 else:
00094 for p in paths:
00095 if p[0] != '/':
00096 raise NonRootPathError(p)
00097 if len(p) > 1:
00098 self.add_name_server(p[1], filter=filter,
00099 dynamic=dynamic)
00100 self.load_servers_from_env(filter=filter, dynamic=dynamic)
00101 if not servers and not paths:
00102 self.load_servers_from_env(filter=filter, dynamic=dynamic)
00103
00104 def __del__(self):
00105
00106 if self._orb_is_mine:
00107 self._orb.shutdown(wait_for_completion=CORBA.FALSE)
00108 self._orb.destroy()
00109
00110 def __str__(self):
00111
00112 return str(self._root)
00113
00114 def add_name_server(self, server, filter=[], dynamic=None):
00115 '''Parse a name server, adding its contents to the tree.
00116
00117 @param server The address of the name server, in standard
00118 address format. e.g. 'localhost',
00119 'localhost:2809', '59.7.0.1'.
00120 @param filter Restrict the parsed objects to only those in this
00121 path. For example, setting filter to [['/',
00122 'localhost', 'host.cxt', 'comp1.rtc']] will
00123 prevent 'comp2.rtc' in the same naming context
00124 from being parsed.
00125 @param dynamic Override the tree-wide dynamic setting. If not provided,
00126 the value given when the tree was created will be used.
00127
00128 '''
00129 if dynamic == None:
00130 dynamic = self._dynamic
00131 self._parse_name_server(server, filter, dynamic=dynamic)
00132
00133 def get_node(self, path):
00134 '''Get a node by path.
00135
00136 @param path A list of path elements pointing to a node in the tree.
00137 For example, ['/', 'localhost', 'dir.host']. The first
00138 element in this path should be the root node's name.
00139
00140 '''
00141 return self._root.get_node(path)
00142
00143 def has_path(self, path):
00144 '''Check if the tree has a path.
00145
00146 @param path A list of path elements pointing to a node in the tree.
00147 For example, ['/', 'localhost', 'dir.host']. The first
00148 element in this path should be the root node's name.
00149
00150 '''
00151 return self._root.has_path(path)
00152
00153 def is_component(self, path):
00154 '''Is the node pointed to by @ref path a component?'''
00155 node = self.get_node(path)
00156 return node.is_component
00157
00158 def is_directory(self, path):
00159 '''Is the node pointed to by @ref path a directory (name servers and
00160 naming contexts)?
00161
00162 '''
00163 node = self.get_node(path)
00164 return node.is_directory
00165
00166 def is_manager(self, path):
00167 '''Is the node pointed to by @ref path a manager?'''
00168 node = self.get_node(path)
00169 return node.is_manager
00170
00171 def is_nameserver(self, path):
00172 '''Is the node pointed to by @ref path a name server (specialisation
00173 of directory nodes)?
00174
00175 '''
00176 node = self.get_node(path)
00177 return node.is_nameserver
00178
00179 def is_unknown(self, path):
00180 '''Is the node pointed to by @ref path an unknown object?'''
00181 node = self.get_node(path)
00182 return node.is_unknown
00183
00184 def is_zombie(self, path):
00185 '''Is the node pointed to by @ref path a zombie object?'''
00186 node = self.get_node(path)
00187 return node.is_zombie
00188
00189 def iterate(self, func, args=None, filter=[]):
00190 '''Call a function on the root node, and recursively all its children.
00191
00192 This is a depth-first iteration.
00193
00194 @param func The function to call. Its declaration must be
00195 'def blag(node, args)', where 'node' is the current node
00196 in the iteration and args is the value of @ref args.
00197 @param args Extra arguments to pass to the function at each iteration.
00198 Pass multiple arguments in as a tuple.
00199 @param filter A list of filters to apply before calling func for each
00200 node in the iteration. If the filter is not True,
00201 @ref func will not be called for that node. Each filter
00202 entry should be a string, representing on of the is_*
00203 properties (is_component, etc), or a function object.
00204 @return The results of the calls to @ref func in a list.
00205
00206 '''
00207 return self._root.iterate(func, args, filter)
00208
00209 def load_servers_from_env(self, filter=[], dynamic=None):
00210 '''Load the name servers environment variable and parse each server in
00211 the list.
00212
00213 @param filter Restrict the parsed objects to only those in this
00214 path. For example, setting filter to [['/',
00215 'localhost', 'host.cxt', 'comp1.rtc']] will
00216 prevent 'comp2.rtc' in the same naming context
00217 from being parsed.
00218 @param dynamic Override the tree-wide dynamic setting. If not provided,
00219 the value given when the tree was created will be used.
00220
00221 '''
00222 if dynamic == None:
00223 dynamic = self._dynamic
00224 if NAMESERVERS_ENV_VAR in os.environ:
00225 servers = [s for s in os.environ[NAMESERVERS_ENV_VAR].split(';') \
00226 if s]
00227 self._parse_name_servers(servers, filter, dynamic)
00228
00229 def give_away_orb(self):
00230 '''Releases ownership of an ORB created by the tree.
00231
00232 This will prevent the ORB being destroyed when the tree is.
00233
00234 '''
00235 self._orb_is_mine = False
00236
00237 def own_orb(self):
00238 '''Claims ownership of an ORB created elsewhere.
00239
00240 This will cause the ORB to be destroyed when the tree is.
00241
00242 '''
00243 self._orb_is_mine = True
00244
00245 @property
00246 def orb(self):
00247 '''The reference to the ORB held by this tree.'''
00248 return self._orb
00249
00250 def _create_orb(self, orb=None):
00251
00252
00253 if orb:
00254 self._orb = orb
00255 self._orb_is_mine = False
00256 else:
00257 if ORB_ARGS_ENV_VAR in os.environ:
00258 orb_args = os.environ[ORB_ARGS_ENV_VAR].split(';')
00259 else:
00260 orb_args = []
00261 self._orb = CORBA.ORB_init(orb_args)
00262 self._orb_is_mine = True
00263
00264 self._poa = self._orb.resolve_initial_references('RootPOA')
00265 self._poa._get_the_POAManager().activate()
00266
00267 def _parse_name_servers(self, servers, filter=[], dynamic=False):
00268
00269 if type(servers) is str:
00270
00271 if servers in self._root.children_names:
00272 return
00273 self._parse_name_server(servers, filter, dynamic=dynamic)
00274 else:
00275 for server in servers:
00276
00277 if server in self._root.children_names:
00278 return
00279 self._parse_name_server(server, filter, dynamic=dynamic)
00280
00281 def _parse_name_server(self, address, filter=[], dynamic=False):
00282
00283 if not filtered(['/', address], filter):
00284 new_ns_node = NameServer(self._orb, address, self._root,
00285 trim_filter(deepcopy(filter), 2), dynamic=dynamic)
00286 self._root._add_child(new_ns_node)
00287
00288
00289
00290