configurationengine/source/cone/storage/webstorage.py
changeset 0 2e8eeb919028
child 3 e7e0ae78773e
equal deleted inserted replaced
-1:000000000000 0:2e8eeb919028
       
     1 #
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description:
       
    15 #
       
    16 ## 
       
    17 # @author Teemu Rytkonen
       
    18 
       
    19 import getpass
       
    20 import StringIO
       
    21 import os
       
    22 import re
       
    23 import copy
       
    24 import logging
       
    25 import httplib
       
    26 import urllib, urllib2
       
    27 import simplejson
       
    28 import posixpath
       
    29 
       
    30 from cone.public import *
       
    31 from cone.carbon import persistentjson, model
       
    32 from cone.storage import authenticate
       
    33 
       
    34 class WebStorage(api.Storage):
       
    35     """
       
    36     A general base class for all storage type classes
       
    37     @param path : the reference to the root of the storage.
       
    38     """
       
    39     
       
    40     def __init__(self, path, mode="a", **kwargs):
       
    41         api.Storage.__init__(self,path)
       
    42         self.mode = mode
       
    43         self.extapi = CarbonExtapi(path, **kwargs)
       
    44         self.persistentmodule = persistentjson
       
    45         
       
    46         # resource cache is a intermediate solution to create a mapping between carbon objects and Configuration project
       
    47         self._resource_cache = None
       
    48         
       
    49 
       
    50     def _create_resource(self, path, object):
       
    51         """
       
    52         Create a new resource (configuration|featurelist) to carbon storage.
       
    53         """
       
    54         # Test the path, whether it is a featurelist or configuration
       
    55         try:
       
    56             object_type = object.meta.get('type')
       
    57         except (TypeError,AttributeError):
       
    58             logging.getLogger('cone').error('Cannot dump configuration %s to webstorage without a type.' % path)
       
    59             return False
       
    60         carbonpath = persistentjson.CarbonResourceMapper().map_confml_resource(object_type, path)
       
    61         if object_type == 'featurelist':
       
    62             # Create a featurelist 
       
    63             success = self.extapi.create_featurelist(carbonpath, object)
       
    64             if success:
       
    65                 self.resource_cache.add_resource_link(path, carbonpath)
       
    66             return success
       
    67         else:
       
    68             # Create a configuration
       
    69             success = self.extapi.create_configuration(carbonpath, object)
       
    70             if success:
       
    71                 self.resource_cache.add_resource_link(path, carbonpath)
       
    72             return success
       
    73 
       
    74     @classmethod
       
    75     def supported_storage(cls,path):
       
    76         """
       
    77         Class method for determing if the given clas supports a storage by given path. 
       
    78         E.g. http://foo.com/
       
    79         @param path:
       
    80         @return: Boolean value. True if the storage of the path is supported. False if not.  
       
    81         """
       
    82         if path.startswith('http://'):
       
    83             return True
       
    84         else:
       
    85             return False
       
    86 
       
    87     @property
       
    88     def resource_cache(self):
       
    89         """
       
    90         Returns a resource cache dictionary of all the resources inside the Carbon storage. Works as an intermediate 
       
    91         solution to link Configuration project concepts to Carbon storage
       
    92         
       
    93         """
       
    94         if not self._resource_cache: 
       
    95             self._resource_cache = ResourceCache()
       
    96             reslist = self.extapi.list_resources("/", True)
       
    97             # Append all resources to resource cache
       
    98             for res in reslist:
       
    99                 self._resource_cache.add_resource(res)
       
   100 #                    if isinstance(res, model.ConfigurationResource):
       
   101 #                        self._resource_cache.add_configuration(res)
       
   102 #                    elif isinstance(res, model.FeatureListResource):
       
   103 #                        self._resource_cache.add_featurelist(res)
       
   104         return self._resource_cache
       
   105 
       
   106     def list_resources(self,path, recurse=False, empty_folders=False):
       
   107         """
       
   108         find the resources under certain path/path 
       
   109         @param path : reference to path where resources are searched
       
   110         @param recurse : defines whether to return resources directly under the path or does the listing recurse to subfolders. 
       
   111         Default value is False. Set to True to enable recursion.
       
   112         @param empty_folders: parameters that defined whether empty folders are included. This parameter is ignored 
       
   113         in WebStorage. 
       
   114         """
       
   115         return self.resource_cache.list_resources(path,recurse)
       
   116 
       
   117 
       
   118     def open_resource(self,path,mode="r"):
       
   119         path = utils.resourceref.remove_begin_slash(path)
       
   120         if self.resource_cache.get_resource_object(path):
       
   121             return self.resource_cache.get_resource_object(path)
       
   122         elif self.resource_cache.get_resource_link(path):
       
   123             path = self.resource_cache.get_resource_link(path)
       
   124         
       
   125 #        path = path.replace(".confml", ".configuration")
       
   126 #        path = utils.resourceref.join_refs([self.get_current_path(), path])
       
   127         try:
       
   128             if self.get_mode(mode) == self.MODE_READ:
       
   129                 strio = self.extapi._get_stringio_from_path(path)
       
   130             elif self.get_mode(mode) == self.MODE_APPEND:
       
   131                 strio = self.extapi._get_stringio_from_path(path)
       
   132                 strio.seek(0, os.SEEK_END)
       
   133             elif self.get_mode(mode) == self.MODE_WRITE:
       
   134                 strio = StringIO.StringIO()
       
   135             else:
       
   136                 raise StorageException("Unrecognized mode %s" % mode)
       
   137             res = WebResource(self,path,mode,strio)
       
   138             self.__opened__(res)
       
   139             return res
       
   140         except KeyError:
       
   141             raise exceptions.NotResource("The given resource is not found %s" % path)
       
   142 
       
   143     def is_resource(self,path):
       
   144         return self.resource_cache.is_resource(path)
       
   145 #        path = path.replace(".confml", ".configuration")
       
   146 #        path = utils.resourceref.join_refs([self.get_current_path(), path])
       
   147 #        try:
       
   148 #            query = urllib.quote(self._get_action_url('is_resource', path))
       
   149 #            self.conn.request("GET", query)
       
   150 #            resp = self.conn.getresponse()
       
   151 #            reader = persistentjson.HasResourceReader()
       
   152 #            return reader.loads(resp.read())
       
   153 #        except exceptions.NotFound:
       
   154 #            return False
       
   155 
       
   156     def save_resource(self, res):
       
   157         """
       
   158         Close resource is no-operation action with webstorage for now
       
   159         """
       
   160         return 
       
   161 
       
   162     def close_resource(self, path):
       
   163         """
       
   164         Close resource is no-operation action with webstorage
       
   165         """
       
   166         return 
       
   167 
       
   168     def export_resources(self, paths, storage, empty_folders=False):
       
   169         for path in paths:
       
   170             if not self.is_resource(path):
       
   171                 logging.getLogger('cone').warning("The given path is not a Resource in this storage %s! Ignoring from export!" % path)
       
   172                 continue
       
   173             wres = storage.open_resource(path,'wb')
       
   174             res  = self.open_resource(path,"rb")
       
   175             wres.write(res.read())
       
   176             wres.close()
       
   177             res.close()
       
   178 
       
   179     def unload(self, path, object):
       
   180         """
       
   181         Dump a given object to the storage (reference is fetched from the object)
       
   182         @param object: The object to dump to the storage, which is expected to be an instance 
       
   183         of Base class.
       
   184         """
       
   185         # Add the current path in front of the given path
       
   186         path = utils.resourceref.join_refs([self.get_current_path(), path])
       
   187         print "unload %s" % path
       
   188         if not isinstance(object, api.Configuration):
       
   189             raise exceptions.StorageException("Cannot dump object type %s" % object.__class__)
       
   190         # Skip the unload storing to storage if the storage is opened in read mode
       
   191         if self.get_mode(self.mode) != api.Storage.MODE_READ:
       
   192             if self.resource_cache.get_resource_link(path):
       
   193                 path = self.resource_cache.get_resource_link(path)
       
   194             else:
       
   195                 """ otherwise create the new resource first before update"""
       
   196                 if self._create_resource(path, object):
       
   197                     path = self.resource_cache.get_resource_link(path)
       
   198                 else:
       
   199                     # Creation failed
       
   200                     logging.getLogger('cone').error('Creation of %s resource failed' % path)
       
   201                     return 
       
   202             data = persistentjson.dumps(object)
       
   203             self.extapi.update_resource(path, data)
       
   204         else:
       
   205             raise exceptions.StorageException("Cannot dump object to readonly storage")
       
   206         return
       
   207 
       
   208     def load(self, path):
       
   209         """
       
   210         Load resource from a path.
       
   211         """
       
   212         # Check if the object is already cached or has a cached link to another resource to load
       
   213         path = utils.resourceref.remove_begin_slash(path)
       
   214         print "load %s" % path
       
   215         if self.resource_cache.get_resource_link(path):
       
   216             path = self.resource_cache.get_resource_link(path)
       
   217         elif self.resource_cache.get_mapped_resource(path):
       
   218             path = self.resource_cache.get_mapped_resource(path)
       
   219         elif not utils.resourceref.get_ext(path) == "confml":
       
   220             raise exceptions.StorageException("Cannot load reference type %s" % utils.resourceref.get_ext(path))
       
   221         else:
       
   222             # Add the current path in front of the given path
       
   223             path = utils.resourceref.join_refs([self.get_current_path(), path])
       
   224             if not self.is_resource(path):
       
   225                 raise exceptions.NotResource("No such %s resource!" % path)
       
   226 
       
   227         res = self.open_resource(path,"r")
       
   228         # read the resource with persistentmodule
       
   229         try:
       
   230             obj = self.persistentmodule.loads(res.read())
       
   231             #obj.set_path(path)
       
   232             res.close()
       
   233             return obj
       
   234         except exceptions.ParseError,e:
       
   235             logging.getLogger('cone').error("Resource %s parsing failed with exception: %s" % (path,e))
       
   236             # returning an empty config in case of xml parsing failure.
       
   237             return api.Configuration(path)
       
   238 
       
   239     def close(self):
       
   240         """ No operation in web storage close """
       
   241         pass
       
   242 
       
   243 
       
   244 class WebResource(api.Resource):
       
   245     def __init__(self,storage,path,mode,handle):
       
   246         api.Resource.__init__(self,storage,path,mode)
       
   247         self.handle = handle
       
   248 
       
   249     def read(self,bytes=0):
       
   250         if bytes == 0:
       
   251             return self.handle.read()
       
   252         else:
       
   253             return self.handle.read(bytes)
       
   254     
       
   255     def write(self,string):
       
   256         if self.get_mode() == api.Storage.MODE_READ:
       
   257             raise exceptions.StorageException("Writing attempted to %s in read-only mode." % self.path)
       
   258         else:
       
   259             self.handle.write(string)
       
   260 
       
   261     def truncate(self,size=0):
       
   262         raise exceptions.NotSupportedException()
       
   263 
       
   264     def flush(self):
       
   265         self.storage.flush_resource(self)
       
   266 
       
   267     def close(self):
       
   268         self.storage.close_resource(self)
       
   269         self.handle.close()
       
   270     
       
   271     def get_size(self):
       
   272         if self.get_mode() == api.Storage.MODE_WRITE:
       
   273             raise exceptions.StorageException("Reading resource size attempted to %s in write-only mode." % self.path)
       
   274         return len(self.handle.getvalue())
       
   275     
       
   276     def getvalue(self):
       
   277         return self.handle.getvalue()
       
   278 
       
   279 
       
   280 class CarbonExtapi(object):
       
   281     ACTIONS = { 'open_resource' : 'get_resource',
       
   282                 'list_resources' : 'list_resources',
       
   283                 'is_resource' : 'has_resource',
       
   284                 'put_resource' : 'put_resource',
       
   285                 'update_resource' : 'update_resource'   }
       
   286     
       
   287     """
       
   288     A general container for Carbon extapi action
       
   289     """
       
   290     def __init__(self, path, **kwargs):
       
   291         self.path = path
       
   292         self.server_path = ''
       
   293         self.service_path = ''
       
   294         if path.startswith('http://'):
       
   295             path = path.replace('http://','',1)
       
   296         pathelems = path.split('/',1)
       
   297         self.server_path = pathelems[0]
       
   298         if len(pathelems) > 1: 
       
   299             self.service_path = pathelems[1]
       
   300         self._username = kwargs.get('username', None)
       
   301         self._password = kwargs.get('password', None)
       
   302         authhandler = authenticate.CarbonAuthHandler()
       
   303         authhandler.add_password(self.username, self.password)
       
   304         self.conn = urllib2.build_opener(urllib2.HTTPCookieProcessor, authhandler, urllib2.ProxyHandler({}))
       
   305         
       
   306     @property
       
   307     def username(self):
       
   308         if self._username == None:
       
   309             self._username = getpass.getuser()
       
   310         return self._username 
       
   311 
       
   312     @property
       
   313     def password(self):
       
   314         if self._password == None:
       
   315             self._password = getpass.getpass()
       
   316         return self._password
       
   317 
       
   318     def checklogin(self):
       
   319         """
       
   320         Checks that we are logged in by loading the main page.
       
   321         If we are not logged in it will redirect us to login page.
       
   322         """
       
   323         loginurl = "http://%(host)s/" % dict(host=self.server_path)
       
   324         loginreq = urllib2.Request(loginurl)
       
   325         print 'Checking login by opening URL %s ...' % loginurl
       
   326         try:
       
   327             resp = self.conn.open(loginreq)
       
   328         except urllib2.URLError, e:
       
   329             print str(e)
       
   330             return False
       
   331         return True
       
   332     
       
   333     def _get_stringio_from_path(self,path):
       
   334         """ 
       
   335         return a StringIO object containing the data under a given path.
       
   336         @return: StringIO buffer
       
   337         @raise exception.NotResource: if the resource under path is not found 
       
   338         """
       
   339         path = utils.resourceref.remove_begin_slash(path)
       
   340         action_url = self._get_action_url('open_resource', path)
       
   341         req = urllib2.Request(action_url)
       
   342         try:
       
   343             resp = self.conn.open(req)
       
   344             bytes = resp.read()
       
   345             strio = StringIO.StringIO(bytes)
       
   346             return strio
       
   347         except urllib2.HTTPError,e:
       
   348             raise exceptions.NotResource("The given path %s could not be retrived from this storage. Server returned status %s." % (path, e))
       
   349 
       
   350     def _get_action_url(self, action, path, **kwargs):
       
   351         path = utils.resourceref.remove_begin_slash(path)
       
   352         return "%s/%s/%s" % (self.path,self.ACTIONS[action], urllib.quote(path))
       
   353     
       
   354     def list_resources_for_type(self,path,type, recurse=False):
       
   355         """
       
   356         find the resources under certain path/path 
       
   357         @param path : reference to path where resources are searched
       
   358         @param type : resources for particular carbon specific type. 
       
   359         @param recurse : defines whether to return resources directly under the path or does the listing recurse to subfolders. 
       
   360         Default value is False. Set to True to enable recursion.
       
   361         """
       
   362         try:
       
   363             path = utils.resourceref.join_refs([path,'.'+type])
       
   364             path = utils.resourceref.remove_begin_slash(path)
       
   365             query = self._get_action_url('list_resources', path)
       
   366             req = urllib2.Request(query)
       
   367             resp = self.conn.open(req)
       
   368             if resp.code == httplib.OK:
       
   369                 bytes = resp.read()
       
   370                 reader = persistentjson.ResourceListReader()
       
   371                 reslist = reader.loads(bytes)
       
   372                 return reslist
       
   373             else:
       
   374                 return []
       
   375         except exceptions.NotFound:
       
   376             return []
       
   377 
       
   378     def list_resources(self,path, recurse=False):
       
   379         """
       
   380         find the resources under certain path/path 
       
   381         @param path : reference to path where resources are searched
       
   382         @param recurse : defines whether to return resources directly under the path or does the listing recurse to subfolders. 
       
   383         Default value is False. Set to True to enable recursion.
       
   384         """
       
   385         try:
       
   386             path = utils.resourceref.remove_begin_slash(path)
       
   387             query = self._get_action_url('list_resources', path)
       
   388             req = urllib2.Request(query)
       
   389             resp = self.conn.open(req)
       
   390             if resp.code == httplib.OK:
       
   391                 bytes = resp.read()
       
   392                 reslist = simplejson.loads(bytes)
       
   393                 return reslist.get('resources',[])
       
   394             else:
       
   395                 return []
       
   396         except exceptions.NotFound:
       
   397             return []
       
   398 
       
   399 
       
   400     def update_resource(self, path, data):
       
   401         """
       
   402         Update a resource to carbon. The resource can be a CarbonConfiguration or FeatureList object.
       
   403         @param object: The object which is dumped to dict with persistentjson and then updated to server. 
       
   404         """
       
   405         try:
       
   406             path = utils.resourceref.remove_begin_slash(path)
       
   407             query = self._get_action_url('update_resource', path)
       
   408             jsondata = simplejson.dumps(data)
       
   409             encdata = urllib.urlencode({'data' : jsondata})
       
   410             req = urllib2.Request(query, encdata)
       
   411             
       
   412             resp = self.conn.open(req)
       
   413             if resp.code == httplib.OK:
       
   414                 bytes = resp.read()
       
   415                 respdata = simplejson.loads(bytes)
       
   416                 success = respdata.get('success') == True
       
   417                 if success:
       
   418                     logging.getLogger('cone').info('Carbon update succeeds to path %s.' % (respdata.get('path')))
       
   419                 else:
       
   420                     logging.getLogger('cone').error('Carbon update %s failed %s' % (path,respdata.get('errors')))
       
   421                 return success
       
   422             else:
       
   423                 logging.getLogger('cone').error('Carbon update %s failed %s: %s' % (path,resp.code, resp))
       
   424                 return False
       
   425         except urllib2.HTTPError,e:
       
   426             utils.log_exception(logging.getLogger('cone'), "HTTPError in %s, %s" % (query,e))
       
   427             return False
       
   428 
       
   429     def create_feature(self,path, feature, parent=None):
       
   430         """
       
   431         Create new Carbon feature based on Feature object.
       
   432         @param path: The path to the featurelist where the feature is created. 
       
   433         @param feature: The feature object 
       
   434         @param parent: A possible parent feature ref
       
   435         """
       
   436         try:
       
   437             path = utils.resourceref.remove_begin_slash(path)
       
   438             query = self._get_action_url('put_resource', path)
       
   439             data = persistentjson.dumps(feature)
       
   440             if parent:
       
   441                 data['parent'] = parent
       
   442             jsondata = simplejson.dumps(data)
       
   443             encdata = urllib.urlencode({'data' : jsondata})
       
   444             req = urllib2.Request(query, encdata)
       
   445             
       
   446             resp = self.conn.open(req)
       
   447             if resp.code == httplib.OK:
       
   448                 bytes = resp.read()
       
   449                 respdata = simplejson.loads(bytes)
       
   450                 success = respdata.get('success') == True
       
   451                 if success:
       
   452                     logging.getLogger('cone').info('New Carbon feature created to path %s.' % (respdata.get('path')))
       
   453                 else:
       
   454                     logging.getLogger('cone').error('Feature %s creation failed %s' % (feature.fqr,respdata.get('errors')))
       
   455                 return success
       
   456             else:
       
   457                 logging.getLogger('cone').error('Feature %s creation failed %s: %s' % (feature.fqr,resp.code, resp))
       
   458                 return False
       
   459         except urllib2.HTTPError,e:
       
   460             utils.log_exception(logging.getLogger('cone'), "HTTPError in %s, %s" % (query,e))
       
   461             return False
       
   462 
       
   463     def create_featurelist(self, path, featurelist):
       
   464         """
       
   465         Create new Carbon featurelist to carbon.
       
   466         @param featurelist: The FeatureList object which is created. 
       
   467         @return: tuple (success, created_path) where success indicates the success of the operation
       
   468         and created_path is the newly created featurelist path on success.
       
   469         """
       
   470         try:
       
   471             path = utils.resourceref.remove_begin_slash(path)
       
   472             query = self._get_action_url('put_resource', path)
       
   473             data = persistentjson.FeatureListCreateWriter().dumps(featurelist)
       
   474             jsondata = simplejson.dumps(data)
       
   475             encdata = urllib.urlencode({'data' : jsondata})
       
   476             req = urllib2.Request(query, encdata)
       
   477             
       
   478             resp = self.conn.open(req)
       
   479             if resp.code == httplib.OK:
       
   480                 bytes = resp.read()
       
   481                 respdata = simplejson.loads(bytes)
       
   482                 success = respdata.get('success') == True
       
   483                 newpath = respdata.get('path')
       
   484                 if success:
       
   485                     logging.getLogger('cone').info('New Carbon featurelist created to path %s.' % (newpath))
       
   486                 else:
       
   487                     logging.getLogger('cone').error('FeatureList %s creation failed %s' % (featurelist.path,respdata.get('errors')))
       
   488                 return (success, newpath)
       
   489             else:
       
   490                 logging.getLogger('cone').error('FeatureList %s creation failed %s: %s' % (featurelist.path,resp.code, resp))
       
   491                 return (False,'')
       
   492         except urllib2.HTTPError,e:
       
   493             utils.log_exception(logging.getLogger('cone'), "HTTPError in %s, %s" % (query,e))
       
   494             return (False,'')
       
   495 
       
   496     def create_configuration(self, path, configuration):
       
   497         """
       
   498         Create new Carbon configuration to carbon.
       
   499         @param path: The path to the configuration 
       
   500         @param configuration: The CarbonConfiguration object
       
   501         @return: tuple (success, created_path) where success indicates the success of the operation
       
   502         and created_path is the newly created configuration path on success.
       
   503         """
       
   504         try:
       
   505             path = utils.resourceref.remove_begin_slash(path)
       
   506             query = self._get_action_url('put_resource', path)
       
   507             data = persistentjson.ConfigurationCreateWriter().dumps(configuration)
       
   508             jsondata = simplejson.dumps(data)
       
   509             encdata = urllib.urlencode({'data' : jsondata})
       
   510             req = urllib2.Request(query, encdata)
       
   511             
       
   512             resp = self.conn.open(req)
       
   513             if resp.code == httplib.OK:
       
   514                 bytes = resp.read()
       
   515                 respdata = simplejson.loads(bytes)
       
   516                 success = respdata.get('success') == True
       
   517                 newpath = respdata.get('path')
       
   518                 if success:
       
   519                     logging.getLogger('cone').info('New Carbon configuration created to path %s.' % (newpath))
       
   520                 else:
       
   521                     logging.getLogger('cone').error('CarbonConfiguration %s creation failed %s' % (configuration.path,respdata.get('errors')))
       
   522                 return (success, newpath)
       
   523             else:
       
   524                 logging.getLogger('cone').error('CarbonConfiguration %s creation failed %s: %s' % (configuration.path,resp.code, resp))
       
   525                 return (False,'')
       
   526         except urllib2.HTTPError,e:
       
   527             utils.log_exception(logging.getLogger('cone'), "HTTPError in %s, %s" % (query,e))
       
   528             return (False,'')
       
   529 
       
   530 class ResourceCache(object):
       
   531     """
       
   532     Resource cache maintains a list of ConE resource names and their actual links to Carbon resources.
       
   533     """
       
   534     def __init__(self):
       
   535         self._cache = {}
       
   536 
       
   537     def add_configuration(self, configuration):
       
   538         """
       
   539         Add a list of Carbon configurations. 
       
   540         """
       
   541         
       
   542         # Create the configuration as a layer and configuration root
       
   543         self._cache[configuration.get_path()] = configuration.path
       
   544         rootconf_path = configuration.name+'.confml'
       
   545         rootconf = model.CarbonConfiguration(rootconf_path)
       
   546         rootconf.include_configuration(configuration.get_path())
       
   547         self._cache[rootconf_path] = rootconf
       
   548 
       
   549     def add_featurelist(self, featurelist):
       
   550         """
       
   551         Add a list of Carbon configurations. 
       
   552         """
       
   553         # Add the feature list under feature list folder 
       
   554         self._cache["featurelists/"+featurelist.get_path()] = featurelist.get_carbon_path()
       
   555         pass
       
   556 
       
   557     def add_resource(self, resourcepath):
       
   558         """
       
   559         Add a resource 
       
   560         """
       
   561         confmlpath = persistentjson.CarbonResourceMapper().map_carbon_resource(resourcepath)
       
   562         self._cache[confmlpath] = resourcepath 
       
   563 
       
   564     def list_resources(self, path, recurse=False):
       
   565         """
       
   566         List ConE resources under certain path 
       
   567         """
       
   568         resources = []
       
   569         path = utils.resourceref.insert_begin_slash(path)
       
   570         for res in self._cache.keys():
       
   571             (respath,resname) = posixpath.split(res)
       
   572             respath = utils.resourceref.insert_begin_slash(respath)
       
   573             if recurse:
       
   574                 if posixpath.normpath(respath).startswith(posixpath.normpath(path)):
       
   575                      resources.append(res)
       
   576             else:
       
   577                 if posixpath.normpath(respath) == posixpath.normpath(path):
       
   578                      resources.append(res)
       
   579         return resources
       
   580 
       
   581     def is_resource(self,path):
       
   582         return self._cache.has_key(path)
       
   583 
       
   584     def get_resource_link(self,path):
       
   585         """
       
   586         Get a the actual Carbon resource link if it is found from cached storage. 
       
   587         """
       
   588         linkpath = self._cache.get(path, None)
       
   589         if isinstance(linkpath, str):
       
   590              return linkpath
       
   591         else:
       
   592             return None
       
   593 
       
   594     def get_mapped_resource(self, path):
       
   595         # Try to make a carbon like resource path for a confml resource
       
   596         if path.startswith('featurelists'):
       
   597             object_type = 'featurelist'
       
   598         elif path.endswith('/root.confml'):
       
   599             object_type = 'configurationlayer'
       
   600         else:
       
   601             object_type = 'configurationroot'
       
   602         carbonpath = persistentjson.CarbonResourceMapper().map_confml_resource(object_type, path)
       
   603         return carbonpath
       
   604 
       
   605     def add_resource_link(self,link, path):
       
   606         """
       
   607         Add a actual Carbon resource link. The link is the key which returns path when asked from get_resource_link.
       
   608         @param link: is linking path to the actual carbon resource path
       
   609         @param path: is the actual carbon path  
       
   610         """
       
   611         self._cache[link] = path
       
   612 
       
   613     def get_resource_object(self,path):
       
   614         """
       
   615         Get a the actual cached Carbon object if it is found. 
       
   616         """
       
   617         cachebj = self._cache.get(path, None)
       
   618         if isinstance(cachebj, model.CarbonConfiguration):
       
   619              return cachebj
       
   620         return None
       
   621