Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python3 

2# Copyright 2020 Canonical Ltd. 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); you may 

5# not use this file except in compliance with the License. You may obtain 

6# a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

13# License for the specific language governing permissions and limitations 

14# under the License. 

15# 

16# For those usages not covered by the Apache License, Version 2.0 please 

17# contact: legal@canonical.com 

18# 

19# To get in touch with the maintainers, please contact: 

20# osm-charmers@lists.launchpad.net 

21## 

22 

23import logging 

24from typing import Any, Dict, List, NoReturn 

25 

26logger = logging.getLogger(__name__) 

27 

28 

29def _validate_data( 

30 config_data: Dict[str, Any], relation_data: Dict[str, Any] 

31) -> NoReturn: 

32 """Validates passed information. 

33 

34 Args: 

35 config_data (Dict[str, Any]): configuration information. 

36 relation_data (Dict[str, Any]): relation information 

37 

38 Raises: 

39 ValueError: when config and/or relation data is not valid. 

40 """ 

41 config_validators = { 

42 "enable_ng_ro": lambda value, _: isinstance(value, bool), 

43 "database_commonkey": lambda value, values: ( 

44 isinstance(value, str) and len(value) > 0 

45 ) 

46 if values.get("enable_ng_ro", True) 

47 else True, 

48 "log_level": lambda value, _: ( 

49 isinstance(value, str) and value in ("INFO", "DEBUG") 

50 ), 

51 "vim_database": lambda value, values: ( 

52 isinstance(value, str) and len(value) > 0 

53 ) 

54 if not values.get("enable_ng_ro", True) 

55 else True, 

56 "ro_database": lambda value, values: (isinstance(value, str) and len(value) > 0) 

57 if not values.get("enable_ng_ro", True) 

58 else True, 

59 "openmano_tenant": lambda value, values: ( 

60 isinstance(value, str) and len(value) > 0 

61 ) 

62 if not values.get("enable_ng_ro", True) 

63 else True, 

64 } 

65 relation_validators = { 

66 "kafka_host": lambda value, _: (isinstance(value, str) and len(value) > 0) 

67 if config_data.get("enable_ng_ro", True) 

68 else True, 

69 "kafka_port": lambda value, _: (isinstance(value, str) and len(value) > 0) 

70 if config_data.get("enable_ng_ro", True) 

71 else True, 

72 "mongodb_connection_string": lambda value, _: ( 

73 isinstance(value, str) and value.startswith("mongodb://") 

74 ) 

75 if config_data.get("enable_ng_ro", True) 

76 else True, 

77 "mysql_host": lambda value, _: (isinstance(value, str) and len(value) > 0) 

78 if not config_data.get("enable_ng_ro", True) 

79 else True, 

80 "mysql_port": lambda value, _: (isinstance(value, int) and value > 0) 

81 if not config_data.get("enable_ng_ro", True) 

82 else True, 

83 "mysql_user": lambda value, _: (isinstance(value, str) and len(value) > 0) 

84 if not config_data.get("enable_ng_ro", True) 

85 else True, 

86 "mysql_password": lambda value, _: (isinstance(value, str) and len(value) > 0) 

87 if not config_data.get("enable_ng_ro", True) 

88 else True, 

89 "mysql_root_password": lambda value, _: ( 

90 isinstance(value, str) and len(value) > 0 

91 ) 

92 if not config_data.get("enable_ng_ro", True) 

93 else True, 

94 } 

95 problems = [] 

96 

97 for key, validator in config_validators.items(): 

98 valid = validator(config_data.get(key), config_data) 

99 

100 if not valid: 

101 problems.append(key) 

102 

103 for key, validator in relation_validators.items(): 

104 valid = validator(relation_data.get(key), relation_data) 

105 

106 if not valid: 

107 problems.append(key) 

108 

109 if len(problems) > 0: 

110 raise ValueError("Errors found in: {}".format(", ".join(problems))) 

111 

112 

113def _make_pod_ports(port: int) -> List[Dict[str, Any]]: 

114 """Generate pod ports details. 

115 

116 Args: 

117 port (int): port to expose. 

118 

119 Returns: 

120 List[Dict[str, Any]]: pod port details. 

121 """ 

122 return [{"name": "ro", "containerPort": port, "protocol": "TCP"}] 

123 

124 

125def _make_pod_envconfig( 

126 config: Dict[str, Any], relation_state: Dict[str, Any] 

127) -> Dict[str, Any]: 

128 """Generate pod environment configuration. 

129 

130 Args: 

131 config (Dict[str, Any]): configuration information. 

132 relation_state (Dict[str, Any]): relation state information. 

133 

134 Returns: 

135 Dict[str, Any]: pod environment configuration. 

136 """ 

137 envconfig = { 

138 # General configuration 

139 "OSMRO_LOG_LEVEL": config["log_level"], 

140 } 

141 

142 if config.get("enable_ng_ro", True): 

143 # Kafka configuration 

144 envconfig["OSMRO_MESSAGE_DRIVER"] = "kafka" 

145 envconfig["OSMRO_MESSAGE_HOST"] = relation_state["kafka_host"] 

146 envconfig["OSMRO_MESSAGE_PORT"] = relation_state["kafka_port"] 

147 

148 # MongoDB configuration 

149 envconfig["OSMRO_DATABASE_DRIVER"] = "mongo" 

150 envconfig["OSMRO_DATABASE_URI"] = relation_state["mongodb_connection_string"] 

151 envconfig["OSMRO_DATABASE_COMMONKEY"] = config["database_commonkey"] 

152 else: 

153 envconfig["RO_DB_HOST"] = relation_state["mysql_host"] 

154 envconfig["RO_DB_OVIM_HOST"] = relation_state["mysql_host"] 

155 envconfig["RO_DB_PORT"] = relation_state["mysql_port"] 

156 envconfig["RO_DB_OVIM_PORT"] = relation_state["mysql_port"] 

157 envconfig["RO_DB_USER"] = relation_state["mysql_user"] 

158 envconfig["RO_DB_OVIM_USER"] = relation_state["mysql_user"] 

159 envconfig["RO_DB_PASSWORD"] = relation_state["mysql_password"] 

160 envconfig["RO_DB_OVIM_PASSWORD"] = relation_state["mysql_password"] 

161 envconfig["RO_DB_ROOT_PASSWORD"] = relation_state["mysql_root_password"] 

162 envconfig["RO_DB_OVIM_ROOT_PASSWORD"] = relation_state["mysql_root_password"] 

163 envconfig["RO_DB_NAME"] = config["ro_database"] 

164 envconfig["RO_DB_OVIM_NAME"] = config["vim_database"] 

165 envconfig["OPENMANO_TENANT"] = config["openmano_tenant"] 

166 

167 return envconfig 

168 

169 

170def _make_startup_probe() -> Dict[str, Any]: 

171 """Generate startup probe. 

172 

173 Returns: 

174 Dict[str, Any]: startup probe. 

175 """ 

176 return { 

177 "exec": {"command": ["/usr/bin/pgrep", "python3"]}, 

178 "initialDelaySeconds": 60, 

179 "timeoutSeconds": 5, 

180 } 

181 

182 

183def _make_readiness_probe(port: int) -> Dict[str, Any]: 

184 """Generate readiness probe. 

185 

186 Args: 

187 port (int): service port. 

188 

189 Returns: 

190 Dict[str, Any]: readiness probe. 

191 """ 

192 return { 

193 "httpGet": { 

194 "path": "/openmano/tenants", 

195 "port": port, 

196 }, 

197 "periodSeconds": 10, 

198 "timeoutSeconds": 5, 

199 "successThreshold": 1, 

200 "failureThreshold": 3, 

201 } 

202 

203 

204def _make_liveness_probe(port: int) -> Dict[str, Any]: 

205 """Generate liveness probe. 

206 

207 Args: 

208 port (int): service port. 

209 

210 Returns: 

211 Dict[str, Any]: liveness probe. 

212 """ 

213 return { 

214 "httpGet": { 

215 "path": "/openmano/tenants", 

216 "port": port, 

217 }, 

218 "initialDelaySeconds": 600, 

219 "periodSeconds": 10, 

220 "timeoutSeconds": 5, 

221 "successThreshold": 1, 

222 "failureThreshold": 3, 

223 } 

224 

225 

226def make_pod_spec( 

227 image_info: Dict[str, str], 

228 config: Dict[str, Any], 

229 relation_state: Dict[str, Any], 

230 app_name: str = "ro", 

231 port: int = 9090, 

232) -> Dict[str, Any]: 

233 """Generate the pod spec information. 

234 

235 Args: 

236 image_info (Dict[str, str]): Object provided by 

237 OCIImageResource("image").fetch(). 

238 config (Dict[str, Any]): Configuration information. 

239 relation_state (Dict[str, Any]): Relation state information. 

240 app_name (str, optional): Application name. Defaults to "ro". 

241 port (int, optional): Port for the container. Defaults to 9090. 

242 

243 Returns: 

244 Dict[str, Any]: Pod spec dictionary for the charm. 

245 """ 

246 if not image_info: 

247 return None 

248 

249 _validate_data(config, relation_state) 

250 

251 ports = _make_pod_ports(port) 

252 env_config = _make_pod_envconfig(config, relation_state) 

253 startup_probe = _make_startup_probe() 

254 readiness_probe = _make_readiness_probe(port) 

255 liveness_probe = _make_liveness_probe(port) 

256 

257 return { 

258 "version": 3, 

259 "containers": [ 

260 { 

261 "name": app_name, 

262 "imageDetails": image_info, 

263 "imagePullPolicy": "Always", 

264 "ports": ports, 

265 "envConfig": env_config, 

266 "kubernetes": { 

267 "startupProbe": startup_probe, 

268 "readinessProbe": readiness_probe, 

269 "livenessProbe": liveness_probe, 

270 }, 

271 } 

272 ], 

273 "kubernetesResources": { 

274 "ingressResources": [], 

275 }, 

276 }