| # |
| # Copyright (C) 2015 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # |
| |
| """Configuration store classes.""" |
| |
| |
| import os |
| import sqlite3 |
| import string |
| |
| from core import properties |
| |
| |
| class Store(properties.PropBase): |
| """A sqlite3-backed dict interface for storing persistent data. |
| |
| Usage: |
| cd = config.Store('data.db') |
| cs['mykey'] = 'my val' |
| print cs['mykey'] |
| 'mykey' |
| """ |
| |
| PREFIX = 'data_' |
| CACHING = True |
| |
| def __init__(self, file_path, table='data'): |
| super(Store, self).__init__() |
| self._conn = None |
| self._path = file_path |
| self._table = table |
| self._initialized = False |
| |
| def _setup(self): |
| if self._initialized: |
| return True |
| if self._path != ':memory:': |
| db_dir = os.path.dirname(self._path) |
| if not os.path.isdir(db_dir): |
| os.makedirs(db_dir) |
| self._conn = sqlite3.connect(self._path) |
| self._conn.execute('create table if not exists %s ' |
| '(key text PRIMARY KEY, val text)' % self._table) |
| self._initialized = True |
| |
| def dict(self): |
| """Dumps the entire table as a dict.""" |
| if not self._initialized: |
| self._setup() |
| c = self._conn.cursor() |
| d = {} |
| for row in c.execute("select key, val from %s" % self._table): |
| d[row[0]] = row[1] |
| return d |
| |
| def _Get(self, key): |
| if not self._initialized: |
| self._setup() |
| c = self._conn.cursor() |
| c.execute("select val from %s where key=?" % self._table, (key, )) |
| ret = c.fetchone() |
| if ret: |
| return ret[0] |
| return None |
| |
| def _Set(self, key, val): |
| if not self._initialized: |
| self._setup() |
| c = self._conn.cursor() |
| c.execute("insert or replace into %s values (?, ?)" % self._table, |
| (key, val)) |
| self._conn.commit() |
| return val |
| |
| def _load(self, key): |
| if key in self.properties(): |
| key = self.PREFIX + key |
| return self._Get(key) |
| |
| def _save(self, key, value): |
| if key in self.properties(): |
| key = self.PREFIX + key |
| return self._Set(key, value) |
| |
| |
| class DictStore(properties.PropBase): |
| """A dict-backed store primarily used for in-memory replacement for other |
| stores in tests. |
| """ |
| REQUIRED_PROPS = {} |
| OPTIONAL_PROPS = {} |
| |
| def __init__(self): |
| super(DictStore, self).__init__() |
| self._d = {} |
| |
| def _save(self, key, value): |
| self._d[key] = value |
| return value |
| |
| def _load(self, key): |
| if not key in self._d: |
| return '' |
| return self._d[key] |
| |
| |
| class FileStore(properties.PropBase): |
| """A file-backed dict interface for storing persistent data. |
| |
| Usage: |
| cd = config.FileStore(os.path.join(PRODUCT_DIR, 'data_dir')) |
| cd['myprefix/mykey'] = 'my val' |
| print cd['myprefix/mykey'] |
| 'my val' |
| """ |
| |
| CACHING = True |
| |
| def __init__(self, path_prefix='config'): |
| self._prefix = path_prefix |
| super(FileStore, self).__init__() |
| |
| def _remove_comments(self, string_): |
| """Returns |string_| with comments removed and whitespace adjusted. |
| |
| Removes whitespace from beginning and end of all lines, |
| newline characters are replaced with ' '. |
| """ |
| value = '' |
| lines = string_.split('\n') |
| for line in lines: |
| stripped_line = line.strip() |
| if len(stripped_line) > 0 and stripped_line[0] == '#': |
| continue |
| value += line + ' ' |
| return value.strip() |
| |
| def _load_from_file(self, keyfile): |
| """Reads in the contents of |keyfile| stripping out comments and |
| newlines |
| """ |
| value = '' |
| if not os.path.exists(keyfile): |
| return value |
| with open(keyfile, 'r') as f: |
| value = f.read() |
| return self._remove_comments(value) |
| |
| def _save_to_file(self, keyfile, value): |
| """Write |value| to |keyfile|""" |
| if type(value) != str: |
| raise TypeError, 'value must be a str' |
| if not os.path.exists(os.path.dirname(keyfile)): |
| os.makedirs(os.path.dirname(keyfile)) |
| with open(keyfile, 'w') as f: |
| # Note, if comments are written, they will not be read back in! |
| f.write(value) |
| return self._remove_comments(value) |
| |
| def _key_to_path(self, key): |
| """Converts a key of [key0/[key2/[.../]]key to a host-compatible path""" |
| return os.path.join(self._prefix, *string.split(key, '/')) |
| |
| def _load(self, key): |
| """Returns the value for |key|""" |
| keyfile = self._key_to_path(key) |
| return self._load_from_file(keyfile) |
| |
| def _save(self, key, value): |
| """Assignes |value| to |key|""" |
| keyfile = self._key_to_path(key) |
| return self._save_to_file(keyfile, value) |
| |
| |
| class ProductFileStore(FileStore): |
| REQUIRED_PROPS = {'name': [], 'brand': [], 'device': [], 'manufacturer': []} |
| OPTIONAL_PROPS = { |
| 'bdk/version': [], 'bdk/buildtype': ['eng', 'user', 'userdebug'], |
| 'bdk/java': ['0', '1'], 'bdk/allowed_environ': [], |
| 'brillo/product_id': [], 'copy_files': [], |
| 'brillo/crash_server': [], 'packages': []} |
| |
| def __init__(self, product_path): |
| if product_path is None: |
| raise ValueError, 'The product path must not be None.' |
| super(ProductFileStore, self).__init__(os.path.join(product_path, |
| 'config')) |