blob: 3e0482df9df761213efa25bc7353e73ae018963a [file] [log] [blame]
Matteo Scandolo56879722017-05-17 21:39:54 -07001import os
2import sys
3import yaml
4import requests
5import default
6from pykwalify.core import Core as PyKwalify
7
Matteo Scandolo6bc017c2017-05-25 18:37:42 -07008DEFAULT_CONFIG_FILE = "/opt/xos/xos_config.yaml"
Matteo Scandolo1879ce72017-05-30 15:45:26 -07009DEFAULT_CONFIG_SCHEMA = 'xos-config-schema.yaml'
Matteo Scandolo56879722017-05-17 21:39:54 -070010INITIALIZED = False
11CONFIG = {}
12
13class Config:
14 """
15 XOS Configuration APIs
16 """
17
18 @staticmethod
Matteo Scandolo1879ce72017-05-30 15:45:26 -070019 def init(config_file=DEFAULT_CONFIG_FILE, config_schema=DEFAULT_CONFIG_SCHEMA):
20
21 # make schema relative to this directory
22 # TODO give the possibility to specify an absolute path
23 config_schema = Config.get_abs_path(config_schema)
24
Matteo Scandolo56879722017-05-17 21:39:54 -070025 global INITIALIZED
26 global CONFIG
27 # the config module can be initialized only one
28 if INITIALIZED:
29 raise Exception('[XOS-Config] Module already initialized')
30 INITIALIZED = True
31
Matteo Scandolo1879ce72017-05-30 15:45:26 -070032 # if XOS_CONFIG_FILE is defined override the config_file
33 # FIXME shouldn't this stay in whatever module call this one? and then just pass the file to the init method
34 if os.environ.get('XOS_CONFIG_FILE'):
35 config_file = os.environ['XOS_CONFIG_FILE']
36
37 # if XOS_CONFIG_SCHEMA is defined override the config_schema
38 # FIXME shouldn't this stay in whatever module call this one? and then just pass the file to the init method
39 if os.environ.get('XOS_CONFIG_SCHEMA'):
40 config_schema = Config.get_abs_path(os.environ['XOS_CONFIG_SCHEMA'])
Matteo Scandolo56879722017-05-17 21:39:54 -070041
42 # if a -C parameter is set in the cli override the config_file
43 # FIXME shouldn't this stay in whatever module call this one? and then just pass the file to the init method
44 if Config.get_cli_param(sys.argv):
Matteo Scandolo1879ce72017-05-30 15:45:26 -070045 config_schema = Config.get_cli_param(sys.argv)
Matteo Scandolo56879722017-05-17 21:39:54 -070046
Matteo Scandolo1879ce72017-05-30 15:45:26 -070047 CONFIG = Config.read_config(config_file, config_schema)
Matteo Scandolo56879722017-05-17 21:39:54 -070048
49 @staticmethod
50 def clear():
51 global INITIALIZED
52 INITIALIZED = False
53
54 @staticmethod
Matteo Scandolo1879ce72017-05-30 15:45:26 -070055 def get_abs_path(path):
56 if os.path.isabs(path):
57 return path
58 return os.path.dirname(os.path.realpath(__file__)) + '/' + path
59
60 @staticmethod
61 def validate_config_format(config_file, config_schema):
62 schema = os.path.abspath(config_schema)
Matteo Scandolo56879722017-05-17 21:39:54 -070063 c = PyKwalify(source_file=config_file, schema_files=[schema])
64 c.validate(raise_exception=True)
65
66 @staticmethod
67 def get_cli_param(args):
68 last = None
69 for arg in args:
70 if last == '-C':
71 return arg
72 last = arg
73
74 @staticmethod
Matteo Scandolo1879ce72017-05-30 15:45:26 -070075 def read_config(config_file, config_schema):
Matteo Scandolo56879722017-05-17 21:39:54 -070076 """
77 Read the configuration file and return a dictionary
78 :param config_file: string
79 :return: dict
80 """
81 if not os.path.exists(config_file):
82 raise Exception('[XOS-Config] Config file not found at: %s' % config_file)
83
Matteo Scandolo1879ce72017-05-30 15:45:26 -070084 if not os.path.exists(config_schema):
85 raise Exception('[XOS-Config] Config schema not found at: %s' % config_schema)
86
Matteo Scandolo56879722017-05-17 21:39:54 -070087 try:
Matteo Scandolo1879ce72017-05-30 15:45:26 -070088 Config.validate_config_format(config_file, config_schema)
Matteo Scandolo56879722017-05-17 21:39:54 -070089 except Exception, e:
90 raise Exception('[XOS-Config] The config format is wrong: %s' % e.msg)
91
92 with open(config_file, 'r') as stream:
93 return yaml.safe_load(stream)
94
95 @staticmethod
96 def get(query):
97 """
98 Read a parameter from the config
99 :param query: a dot separated selector for configuration options (eg: database.username)
100 :return: the requested parameter in any format the parameter is specified
101 """
102 global INITIALIZED
103 global CONFIG
104
105 if not INITIALIZED:
106 raise Exception('[XOS-Config] Module has not been initialized')
107
108 val = Config.get_param(query, CONFIG)
109 if not val:
110 val = Config.get_param(query, default.DEFAULT_VALUES)
111 if not val:
Matteo Scandolo1879ce72017-05-30 15:45:26 -0700112 # TODO if no val return none
113 # raise Exception('[XOS-Config] Config does not have a value (or a default) parameter %s' % query)
114 return None
Matteo Scandolo56879722017-05-17 21:39:54 -0700115 return val
116
117 @staticmethod
118 def get_param(query, config):
119 """
120 Search for a parameter in config's first level, other call get_nested_param
121 :param query: a dot separated selector for configuration options (eg: database.username)
122 :param config: the config source to read from (can be the config file or the defaults)
123 :return: the requested parameter in any format the parameter is specified
124 """
125 keys = query.split('.')
126 if len(keys) == 1:
127 key = keys[0]
128 if not config.has_key(key):
129 return None
130 return config[key]
131 else:
132 return Config.get_nested_param(keys, config)
133
134 @staticmethod
135 def get_nested_param(keys, config):
136 """
137
138 :param keys: a list of descending selector
139 :param config: the config source to read from (can be the config file or the defaults)
140 :return: the requested parameter in any format the parameter is specified
141 """
142 param = config
143 for k in keys:
144 if not param.has_key(k):
145 return None
146 param = param[k]
147 return param
148
149 @staticmethod
150 def get_service_list():
151 """
152 Query registrator to get the list of services
153 NOTE: we assume that consul is a valid URL
154 :return: a list of service names
155 """
156 service_dict = requests.get('http://consul:8500/v1/catalog/services').json()
157 service_list = []
158 for s in service_dict:
159 service_list.append(s)
160 return service_list
161
162 @staticmethod
163 def get_service_info(service_name):
164 """
165 Query registrator to get the details about a service
166 NOTE: we assume that consul is a valid URL
167 :param service_name: the name of the service, can be retrieved from get_service_list
168 :return: the informations about a service
169 """
170 response = requests.get('http://consul:8500/v1/catalog/service/%s' % service_name)
171 if not response.ok:
172 raise Exception('[XOS-Config] Registrator is down')
173 service = response.json()
174 if not service or len(service) == 0:
175 raise Exception('[XOS-Config] The service missing-service looking for does not exist')
176 return {
177 'name': service[0]['ServiceName'],
178 'url': service[0]['ServiceAddress'],
179 'port': service[0]['ServicePort']
180 }
181
182 @staticmethod
183 def get_service_endpoint(service_name):
184 """
185 Query registrator to get the details about a service and return the endpoint in for of a string
186 :param service_name: the name of the service, can be retrieved from get_service_list
187 :return: the endpoint of the service
188 """
189 service = Config.get_service_info(service_name)
190 return 'http://%s:%s' % (service['url'], service['port'])
191
Matteo Scandolo56879722017-05-17 21:39:54 -0700192if __name__ == '__main__':
Matteo Scandolo1879ce72017-05-30 15:45:26 -0700193 Config.init()