Matteo Scandolo | d2044a4 | 2017-08-07 16:08:28 -0700 | [diff] [blame] | 1 | # Copyright 2017-present Open Networking Foundation |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | |
Scott Baker | 89704b1 | 2019-05-07 16:42:11 -0700 | [diff] [blame] | 15 | import datetime |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 16 | import inspect |
| 17 | from apistats import REQUEST_COUNT, track_request_time |
| 18 | import grpc |
Scott Baker | 75e9d56 | 2019-02-22 09:34:00 -0800 | [diff] [blame] | 19 | from authhelper import XOSAuthHelperMixin |
| 20 | from decorators import translate_exceptions, require_authentication |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 21 | from xos.exceptions import XOSNotAuthenticated |
| 22 | from core.models import ServiceInstance |
| 23 | from django.db.models import F, Q |
Scott Baker | df2a0ac | 2019-06-07 11:56:20 -0700 | [diff] [blame^] | 24 | from django.db import connection |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 25 | import django.apps |
| 26 | from django.contrib.auth import authenticate as django_authenticate |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 27 | import fnmatch |
Scott Baker | afdc468 | 2017-02-14 14:49:05 -0800 | [diff] [blame] | 28 | import os |
| 29 | import sys |
Scott Baker | b96ba43 | 2018-02-26 09:53:48 -0800 | [diff] [blame] | 30 | from protos import utility_pb2, utility_pb2_grpc |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 31 | from google.protobuf.empty_pb2 import Empty |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 32 | from importlib import import_module |
Matteo Scandolo | d920855 | 2017-06-21 14:15:16 -0700 | [diff] [blame] | 33 | from django.conf import settings |
Scott Baker | 89704b1 | 2019-05-07 16:42:11 -0700 | [diff] [blame] | 34 | from xosconfig import Config |
| 35 | from multistructlog import create_logger |
| 36 | |
| 37 | log = create_logger(Config().get("logging")) |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 38 | |
Matteo Scandolo | d920855 | 2017-06-21 14:15:16 -0700 | [diff] [blame] | 39 | SessionStore = import_module(settings.SESSION_ENGINE).SessionStore |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 40 | |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 41 | |
Scott Baker | afdc468 | 2017-02-14 14:49:05 -0800 | [diff] [blame] | 42 | # The Tosca engine expects to be run from /opt/xos/tosca/ or equivalent. It |
| 43 | # needs some sys.path fixing up. |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 44 | |
Scott Baker | afdc468 | 2017-02-14 14:49:05 -0800 | [diff] [blame] | 45 | currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) |
| 46 | toscadir = os.path.join(currentdir, "../tosca") |
| 47 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 48 | |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 49 | def is_internal_model(model): |
| 50 | """ things to be excluded from the dirty_models endpoints """ |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 51 | if "django" in model.__module__: |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 52 | return True |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 53 | if "cors" in model.__module__: |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 54 | return True |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 55 | if "contenttypes" in model.__module__: |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 56 | return True |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 57 | if "core.models.journal" in model.__module__: # why? |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 58 | return True |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 59 | if "core.models.project" in model.__module__: # why? |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 60 | return True |
| 61 | return False |
| 62 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 63 | |
Matteo Scandolo | d920855 | 2017-06-21 14:15:16 -0700 | [diff] [blame] | 64 | def get_xproto(folder): |
| 65 | matches = [] |
| 66 | for root, dirnames, filenames in os.walk(folder): |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 67 | for filename in fnmatch.filter(filenames, "*.xproto"): |
Matteo Scandolo | d920855 | 2017-06-21 14:15:16 -0700 | [diff] [blame] | 68 | matches.append(os.path.join(root, filename)) |
| 69 | return matches |
| 70 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 71 | |
Scott Baker | 75e9d56 | 2019-02-22 09:34:00 -0800 | [diff] [blame] | 72 | class UtilityService(utility_pb2_grpc.utilityServicer, XOSAuthHelperMixin): |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 73 | def __init__(self, thread_pool): |
| 74 | self.thread_pool = thread_pool |
Scott Baker | 75e9d56 | 2019-02-22 09:34:00 -0800 | [diff] [blame] | 75 | XOSAuthHelperMixin.__init__(self) |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 76 | |
| 77 | def stop(self): |
| 78 | pass |
| 79 | |
Matteo Scandolo | 1d01b69 | 2018-10-03 15:53:36 -0700 | [diff] [blame] | 80 | @translate_exceptions("Utilities", "Login") |
| 81 | @track_request_time("Utilities", "Login") |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 82 | def Login(self, request, context): |
| 83 | if not request.username: |
Scott Baker | f09d38a | 2017-04-28 14:23:05 -0700 | [diff] [blame] | 84 | raise XOSNotAuthenticated("No username") |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 85 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 86 | u = django_authenticate(username=request.username, password=request.password) |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 87 | if not u: |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 88 | raise XOSNotAuthenticated( |
| 89 | "Failed to authenticate user %s" % request.username |
| 90 | ) |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 91 | |
| 92 | session = SessionStore() |
| 93 | auth = {"username": request.username, "password": request.password} |
| 94 | session["auth"] = auth |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 95 | session["_auth_user_id"] = u.pk |
| 96 | session["_auth_user_backend"] = u.backend |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 97 | session.save() |
| 98 | |
| 99 | response = utility_pb2.LoginResponse() |
| 100 | response.sessionid = session.session_key |
| 101 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 102 | REQUEST_COUNT.labels("xos-core", "Utilities", "Login", grpc.StatusCode.OK).inc() |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 103 | return response |
| 104 | |
Matteo Scandolo | 1d01b69 | 2018-10-03 15:53:36 -0700 | [diff] [blame] | 105 | @translate_exceptions("Utilities", "Logout") |
| 106 | @track_request_time("Utilities", "Logout") |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 107 | def Logout(self, request, context): |
| 108 | for (k, v) in context.invocation_metadata(): |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 109 | if k.lower() == "x-xossession": |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 110 | s = SessionStore(session_key=v) |
| 111 | if "_auth_user_id" in s: |
| 112 | del s["_auth_user_id"] |
| 113 | s.save() |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 114 | REQUEST_COUNT.labels("xos-core", "Utilities", "Login", grpc.StatusCode.OK).inc() |
Scott Baker | dab1119 | 2017-02-03 16:42:14 -0800 | [diff] [blame] | 115 | return Empty() |
| 116 | |
Matteo Scandolo | 1d01b69 | 2018-10-03 15:53:36 -0700 | [diff] [blame] | 117 | @translate_exceptions("Utilities", "NoOp") |
| 118 | @track_request_time("Utilities", "NoOp") |
Scott Baker | 5ebcd28 | 2017-03-16 16:11:20 -0700 | [diff] [blame] | 119 | def NoOp(self, request, context): |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 120 | REQUEST_COUNT.labels("xos-core", "Utilities", "NoOp", grpc.StatusCode.OK).inc() |
Scott Baker | 5ebcd28 | 2017-03-16 16:11:20 -0700 | [diff] [blame] | 121 | return Empty() |
| 122 | |
Matteo Scandolo | 1d01b69 | 2018-10-03 15:53:36 -0700 | [diff] [blame] | 123 | @translate_exceptions("Utilities", "AuthenticatedNoOp") |
| 124 | @track_request_time("Utilities", "AuthenticatedNoOp") |
Scott Baker | 75e9d56 | 2019-02-22 09:34:00 -0800 | [diff] [blame] | 125 | @require_authentication |
Scott Baker | 1677821 | 2017-05-04 14:35:00 -0700 | [diff] [blame] | 126 | def AuthenticatedNoOp(self, request, context): |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 127 | REQUEST_COUNT.labels( |
| 128 | "xos-core", "Utilities", "AuthenticatedNoOp", grpc.StatusCode.OK |
| 129 | ).inc() |
Scott Baker | 1677821 | 2017-05-04 14:35:00 -0700 | [diff] [blame] | 130 | return Empty() |
| 131 | |
Matteo Scandolo | 1d01b69 | 2018-10-03 15:53:36 -0700 | [diff] [blame] | 132 | @translate_exceptions("Utilities", "ListDirtyModels") |
| 133 | @track_request_time("Utilities", "ListDirtyModels") |
Scott Baker | 75e9d56 | 2019-02-22 09:34:00 -0800 | [diff] [blame] | 134 | @require_authentication |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 135 | def ListDirtyModels(self, request, context): |
| 136 | dirty_models = utility_pb2.ModelList() |
| 137 | |
| 138 | models = django.apps.apps.get_models() |
| 139 | for model in models: |
| 140 | if is_internal_model(model): |
| 141 | continue |
| 142 | fieldNames = [x.name for x in model._meta.fields] |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 143 | if ("enacted" not in fieldNames) or ("updated" not in fieldNames): |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 144 | continue |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 145 | if (request.class_name) and ( |
| 146 | not fnmatch.fnmatch(model.__name__, request.class_name) |
| 147 | ): |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 148 | continue |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 149 | objs = model.objects.filter(Q(enacted__lt=F("updated")) | Q(enacted=None)) |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 150 | for obj in objs: |
| 151 | item = dirty_models.items.add() |
| 152 | item.class_name = model.__name__ |
| 153 | item.id = obj.id |
| 154 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 155 | REQUEST_COUNT.labels( |
| 156 | "xos-core", "Utilities", "ListDirtyModels", grpc.StatusCode.OK |
| 157 | ).inc() |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 158 | return dirty_models |
| 159 | |
Matteo Scandolo | 1d01b69 | 2018-10-03 15:53:36 -0700 | [diff] [blame] | 160 | @translate_exceptions("Utilities", "SetDirtyModels") |
| 161 | @track_request_time("Utilities", "SetDirtyModels") |
Scott Baker | 75e9d56 | 2019-02-22 09:34:00 -0800 | [diff] [blame] | 162 | @require_authentication |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 163 | def SetDirtyModels(self, request, context): |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 164 | user = self.authenticate(context, required=True) |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 165 | |
| 166 | dirty_models = utility_pb2.ModelList() |
| 167 | |
| 168 | models = django.apps.apps.get_models() |
| 169 | for model in models: |
| 170 | if is_internal_model(model): |
| 171 | continue |
| 172 | fieldNames = [x.name for x in model._meta.fields] |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 173 | if ("enacted" not in fieldNames) or ("updated" not in fieldNames): |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 174 | continue |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 175 | if (request.class_name) and ( |
| 176 | not fnmatch.fnmatch(model.__name__, request.class_name) |
| 177 | ): |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 178 | continue |
| 179 | objs = model.objects.all() |
| 180 | for obj in objs: |
| 181 | try: |
Scott Baker | f6a7a48 | 2018-04-05 12:13:08 -0700 | [diff] [blame] | 182 | obj.caller = user |
| 183 | obj.save() |
| 184 | item = dirty_models.items.add() |
| 185 | item.class_name = model.__name__ |
| 186 | item.id = obj.id |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 187 | except Exception as e: |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 188 | item = dirty_models.items.add() |
| 189 | item.class_name = model.__name__ |
| 190 | item.id = obj.id |
| 191 | item.info = str(e) |
| 192 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 193 | REQUEST_COUNT.labels( |
| 194 | "xos-core", "Utilities", "SetDirtyModels", grpc.StatusCode.OK |
| 195 | ).inc() |
Scott Baker | 21d5e4f | 2017-03-25 11:00:33 -0700 | [diff] [blame] | 196 | return dirty_models |
| 197 | |
Matteo Scandolo | 1d01b69 | 2018-10-03 15:53:36 -0700 | [diff] [blame] | 198 | @translate_exceptions("Utilities", "GetXproto") |
| 199 | @track_request_time("Utilities", "GetXproto") |
Scott Baker | 75e9d56 | 2019-02-22 09:34:00 -0800 | [diff] [blame] | 200 | # TODO(smbaker): Tosca engine calls this without authentication |
Matteo Scandolo | d920855 | 2017-06-21 14:15:16 -0700 | [diff] [blame] | 201 | def GetXproto(self, request, context): |
| 202 | res = utility_pb2.XProtos() |
| 203 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 204 | core_dir = os.path.abspath( |
| 205 | os.path.dirname(os.path.realpath(__file__)) + "/../core/models/" |
| 206 | ) |
Matteo Scandolo | d920855 | 2017-06-21 14:15:16 -0700 | [diff] [blame] | 207 | core_xprotos = get_xproto(core_dir) |
| 208 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 209 | service_dir = os.path.abspath( |
| 210 | os.path.dirname(os.path.realpath(__file__)) + "/../services" |
| 211 | ) |
Matteo Scandolo | d920855 | 2017-06-21 14:15:16 -0700 | [diff] [blame] | 212 | services_xprotos = get_xproto(service_dir) |
| 213 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 214 | dynamic_service_dir = os.path.abspath( |
| 215 | os.path.dirname(os.path.realpath(__file__)) + "/../dynamic_services" |
| 216 | ) |
Scott Baker | b06e3e0 | 2017-12-12 11:05:53 -0800 | [diff] [blame] | 217 | dynamic_services_xprotos = get_xproto(dynamic_service_dir) |
| 218 | |
| 219 | xprotos = core_xprotos + services_xprotos + dynamic_services_xprotos |
Matteo Scandolo | d920855 | 2017-06-21 14:15:16 -0700 | [diff] [blame] | 220 | |
| 221 | xproto = "" |
| 222 | |
| 223 | for f in xprotos: |
| 224 | content = open(f).read() |
| 225 | xproto += "\n" |
| 226 | xproto += content |
| 227 | |
| 228 | res.xproto = xproto |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 229 | REQUEST_COUNT.labels( |
| 230 | "xos-core", "Utilities", "GetXproto", grpc.StatusCode.OK |
| 231 | ).inc() |
Matteo Scandolo | d920855 | 2017-06-21 14:15:16 -0700 | [diff] [blame] | 232 | return res |
| 233 | |
Matteo Scandolo | 1d01b69 | 2018-10-03 15:53:36 -0700 | [diff] [blame] | 234 | @translate_exceptions("Utilities", "GetPopulatedServiceInstances") |
| 235 | @track_request_time("Utilities", "GetPopulatedServiceInstances") |
Scott Baker | 75e9d56 | 2019-02-22 09:34:00 -0800 | [diff] [blame] | 236 | @require_authentication |
Matteo Scandolo | 488c88e | 2018-02-01 11:52:33 -0800 | [diff] [blame] | 237 | def GetPopulatedServiceInstances(self, request, context): |
| 238 | """ |
| 239 | Return a service instance with provider and subsciber service instance ids |
| 240 | """ |
| 241 | response = utility_pb2.PopulatedServiceInstance() |
| 242 | |
| 243 | si = ServiceInstance.objects.get(id=request.id) |
| 244 | |
Matteo Scandolo | 488c88e | 2018-02-01 11:52:33 -0800 | [diff] [blame] | 245 | # populate the response object |
| 246 | response.id = si.id |
| 247 | response.leaf_model_name = si.leaf_model_name |
| 248 | response.owner_id = si.owner_id |
| 249 | |
| 250 | if si.name: |
| 251 | response.name = si.name |
| 252 | |
| 253 | # find links |
| 254 | provided_links = si.provided_links.all() |
| 255 | subscribed_links = si.subscribed_links.all() |
| 256 | |
| 257 | # import pdb; pdb.set_trace() |
| 258 | |
| 259 | for l in provided_links: |
| 260 | response.provided_service_instances.append(l.subscriber_service_instance.id) |
| 261 | |
| 262 | for l in subscribed_links: |
| 263 | if l.subscriber_service_instance: |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 264 | response.subscribed_service_instances.append( |
| 265 | l.provider_service_instance_id |
| 266 | ) |
Matteo Scandolo | 488c88e | 2018-02-01 11:52:33 -0800 | [diff] [blame] | 267 | elif l.subscriber_service: |
| 268 | response.subscribed_service.append(l.subscriber_service.id) |
| 269 | elif l.subscriber_network: |
| 270 | response.subscribed_network.append(l.subscriber_network.id) |
| 271 | |
Zack Williams | 045b63d | 2019-01-22 16:30:57 -0700 | [diff] [blame] | 272 | REQUEST_COUNT.labels( |
| 273 | "xos-core", "Utilities", "GetPopulatedServiceInstances", grpc.StatusCode.OK |
| 274 | ).inc() |
Matteo Scandolo | 488c88e | 2018-02-01 11:52:33 -0800 | [diff] [blame] | 275 | return response |
Scott Baker | 89704b1 | 2019-05-07 16:42:11 -0700 | [diff] [blame] | 276 | |
Scott Baker | df2a0ac | 2019-06-07 11:56:20 -0700 | [diff] [blame^] | 277 | @translate_exceptions("Utilities", "GetVersion") |
| 278 | @track_request_time("Utilities", "GetVersion") |
Scott Baker | 89704b1 | 2019-05-07 16:42:11 -0700 | [diff] [blame] | 279 | def GetVersion(self, request, context): |
| 280 | res = utility_pb2.VersionInfo() |
| 281 | |
| 282 | try: |
| 283 | res.version = open("/opt/xos/VERSION").readline().strip() |
| 284 | except Exception: |
| 285 | log.exception("Exception while determining build version") |
| 286 | res.version = "unknown" |
| 287 | |
| 288 | try: |
| 289 | res.gitCommit = open("/opt/xos/COMMIT").readline().strip() |
| 290 | res.buildTime = datetime.datetime.utcfromtimestamp( |
| 291 | os.stat("/opt/xos/COMMIT").st_ctime).strftime("%Y-%m-%dT%H:%M:%SZ") |
| 292 | except Exception: |
| 293 | log.exception("Exception while determining build information") |
| 294 | res.buildDate = "unknown" |
| 295 | res.gitCommit = "unknown" |
| 296 | |
| 297 | res.pythonVersion = sys.version.split("\n")[0].strip() |
| 298 | res.os = os.uname()[0].lower() |
| 299 | res.arch = os.uname()[4].lower() |
| 300 | |
Scott Baker | 89704b1 | 2019-05-07 16:42:11 -0700 | [diff] [blame] | 301 | REQUEST_COUNT.labels( |
| 302 | "xos-core", "Utilities", "GetVersion", grpc.StatusCode.OK |
| 303 | ).inc() |
| 304 | return res |
Scott Baker | df2a0ac | 2019-06-07 11:56:20 -0700 | [diff] [blame^] | 305 | |
| 306 | @translate_exceptions("Utilities", "GetDatabaseInfo") |
| 307 | @track_request_time("Utilities", "GetDatabaseInfo") |
| 308 | def GetDatabaseInfo(self, request, context): |
| 309 | res = utility_pb2.DatabaseInfo() |
| 310 | |
| 311 | res.name = settings.DB["NAME"] |
| 312 | res.connection = "%s:%s" % (settings.DB["HOST"], settings.DB["PORT"]) |
| 313 | |
| 314 | # Start by assuming the db is operational, then we'll perform some tests |
| 315 | # to make sure it's working as we expect. |
| 316 | res.status = res.OPERATIONAL |
| 317 | |
| 318 | # TODO(smbaker): Think about whether these are postgres-specific checks and what might happen |
| 319 | # if another db is configured. |
| 320 | |
| 321 | try: |
| 322 | server_version = connection.cursor().connection.server_version |
| 323 | # example: '100003' for postgres 10.3 |
| 324 | res.version = "%d.%d" % (server_version/10000, server_version % 10000) |
| 325 | except Exception: |
| 326 | res.version = "Unknown" |
| 327 | res.status = res.ERROR |
| 328 | |
| 329 | if res.status == res.OPERATIONAL: |
| 330 | # Try performing a simple query that evaluates a constant. This will prove we are talking |
| 331 | # to the database. |
| 332 | try: |
| 333 | cursor = connection.cursor() |
| 334 | cursor.execute("select 1") |
| 335 | result = cursor.fetchone() |
| 336 | assert(len(result) == 1) |
| 337 | assert(result[0] == 1) |
| 338 | except Exception: |
| 339 | res.status = res.ERROR |
| 340 | |
| 341 | REQUEST_COUNT.labels( |
| 342 | "xos-core", "Utilities", "GetDatabaseInfo", grpc.StatusCode.OK |
| 343 | ).inc() |
| 344 | return res |