github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/rest_api/sawtooth_rest_api/rest_api.py (about) 1 # Copyright 2016 Intel Corporation 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 # ------------------------------------------------------------------------------ 15 16 import os 17 import sys 18 import logging 19 import asyncio 20 import argparse 21 from urllib.parse import urlparse 22 import platform 23 import pkg_resources 24 from aiohttp import web 25 26 from zmq.asyncio import ZMQEventLoop 27 from pyformance import MetricsRegistry 28 from pyformance.reporters import InfluxReporter 29 30 from sawtooth_sdk.processor.log import init_console_logging 31 from sawtooth_sdk.processor.log import log_configuration 32 from sawtooth_sdk.processor.config import get_log_config 33 from sawtooth_sdk.processor.config import get_log_dir 34 from sawtooth_sdk.processor.config import get_config_dir 35 from sawtooth_rest_api.messaging import Connection 36 from sawtooth_rest_api.route_handlers import RouteHandler 37 from sawtooth_rest_api.state_delta_subscription_handler \ 38 import StateDeltaSubscriberHandler 39 from sawtooth_rest_api.config import load_default_rest_api_config 40 from sawtooth_rest_api.config import load_toml_rest_api_config 41 from sawtooth_rest_api.config import merge_rest_api_config 42 from sawtooth_rest_api.config import RestApiConfig 43 44 45 LOGGER = logging.getLogger(__name__) 46 DISTRIBUTION_NAME = 'sawtooth-rest-api' 47 48 49 def parse_args(args): 50 """Parse command line flags added to `rest_api` command. 51 """ 52 parser = argparse.ArgumentParser( 53 description='Starts the REST API application and connects to a ' 54 'specified validator.') 55 56 parser.add_argument('-B', '--bind', 57 help='identify host and port for API to run on \ 58 default: http://localhost:8008)', 59 action='append') 60 parser.add_argument('-C', '--connect', 61 help='specify URL to connect to a running validator') 62 parser.add_argument('-t', '--timeout', 63 help='set time (in seconds) to wait for validator \ 64 response') 65 parser.add_argument('--client-max-size', 66 type=int, 67 help='the max size (in bytes) of a request body') 68 parser.add_argument('-v', '--verbose', 69 action='count', 70 default=0, 71 help='enable more verbose output to stderr') 72 parser.add_argument('--opentsdb-url', 73 help='specify host and port for Open TSDB database \ 74 used for metrics') 75 parser.add_argument('--opentsdb-db', 76 help='specify name of database for storing metrics') 77 78 try: 79 version = pkg_resources.get_distribution(DISTRIBUTION_NAME).version 80 except pkg_resources.DistributionNotFound: 81 version = 'UNKNOWN' 82 83 parser.add_argument( 84 '-V', '--version', 85 action='version', 86 version=(DISTRIBUTION_NAME + ' (Hyperledger Sawtooth) version {}') 87 .format(version), 88 help='display version information') 89 90 return parser.parse_args(args) 91 92 93 def start_rest_api(host, port, connection, timeout, registry, 94 client_max_size=None): 95 """Builds the web app, adds route handlers, and finally starts the app. 96 """ 97 loop = asyncio.get_event_loop() 98 connection.open() 99 app = web.Application(loop=loop, client_max_size=client_max_size) 100 app.on_cleanup.append(lambda app: connection.close()) 101 102 # Add routes to the web app 103 LOGGER.info('Creating handlers for validator at %s', connection.url) 104 105 handler = RouteHandler(loop, connection, timeout, registry) 106 107 app.router.add_post('/batches', handler.submit_batches) 108 app.router.add_get('/batch_statuses', handler.list_statuses) 109 app.router.add_post('/batch_statuses', handler.list_statuses) 110 111 app.router.add_get('/state', handler.list_state) 112 app.router.add_get('/state/{address}', handler.fetch_state) 113 114 app.router.add_get('/blocks', handler.list_blocks) 115 app.router.add_get('/blocks/{block_id}', handler.fetch_block) 116 117 app.router.add_get('/batches', handler.list_batches) 118 app.router.add_get('/batches/{batch_id}', handler.fetch_batch) 119 120 app.router.add_get('/transactions', handler.list_transactions) 121 app.router.add_get( 122 '/transactions/{transaction_id}', 123 handler.fetch_transaction) 124 125 app.router.add_get('/receipts', handler.list_receipts) 126 app.router.add_post('/receipts', handler.list_receipts) 127 128 app.router.add_get('/peers', handler.fetch_peers) 129 app.router.add_get('/status', handler.fetch_status) 130 131 subscriber_handler = StateDeltaSubscriberHandler(connection) 132 app.router.add_get('/subscriptions', subscriber_handler.subscriptions) 133 app.on_shutdown.append(lambda app: subscriber_handler.on_shutdown()) 134 135 # Start app 136 LOGGER.info('Starting REST API on %s:%s', host, port) 137 138 web.run_app( 139 app, 140 host=host, 141 port=port, 142 access_log=LOGGER, 143 access_log_format='%r: %s status, %b size, in %Tf s') 144 145 146 def load_rest_api_config(first_config): 147 default_config = load_default_rest_api_config() 148 config_dir = get_config_dir() 149 conf_file = os.path.join(config_dir, 'rest_api.toml') 150 151 toml_config = load_toml_rest_api_config(conf_file) 152 return merge_rest_api_config( 153 configs=[first_config, toml_config, default_config]) 154 155 156 class MetricsRegistryWrapper(): 157 def __init__(self, registry): 158 self._registry = registry 159 160 def gauge(self, name): 161 return self._registry.gauge( 162 ''.join([name, ',host=', platform.node()])) 163 164 def counter(self, name): 165 return self._registry.counter( 166 ''.join([name, ',host=', platform.node()])) 167 168 def timer(self, name): 169 return self._registry.timer( 170 ''.join([name, ',host=', platform.node()])) 171 172 173 def main(): 174 loop = ZMQEventLoop() 175 asyncio.set_event_loop(loop) 176 177 connection = None 178 try: 179 opts = parse_args(sys.argv[1:]) 180 opts_config = RestApiConfig( 181 bind=opts.bind, 182 connect=opts.connect, 183 timeout=opts.timeout, 184 opentsdb_url=opts.opentsdb_url, 185 opentsdb_db=opts.opentsdb_db, 186 client_max_size=opts.client_max_size) 187 rest_api_config = load_rest_api_config(opts_config) 188 url = None 189 if "tcp://" not in rest_api_config.connect: 190 url = "tcp://" + rest_api_config.connect 191 else: 192 url = rest_api_config.connect 193 194 connection = Connection(url) 195 196 log_config = get_log_config(filename="rest_api_log_config.toml") 197 198 # If no toml, try loading yaml 199 if log_config is None: 200 log_config = get_log_config(filename="rest_api_log_config.yaml") 201 202 if log_config is not None: 203 log_configuration(log_config=log_config) 204 else: 205 log_dir = get_log_dir() 206 log_configuration(log_dir=log_dir, name="rest_api") 207 init_console_logging(verbose_level=opts.verbose) 208 209 try: 210 host, port = rest_api_config.bind[0].split(":") 211 port = int(port) 212 except ValueError as e: 213 print("Unable to parse binding {}: Must be in the format" 214 " host:port".format(rest_api_config.bind[0])) 215 sys.exit(1) 216 217 wrapped_registry = None 218 if rest_api_config.opentsdb_url: 219 LOGGER.info("Adding metrics reporter: url=%s, db=%s", 220 rest_api_config.opentsdb_url, 221 rest_api_config.opentsdb_db) 222 223 url = urlparse(rest_api_config.opentsdb_url) 224 proto, db_server, db_port, = url.scheme, url.hostname, url.port 225 226 registry = MetricsRegistry() 227 wrapped_registry = MetricsRegistryWrapper(registry) 228 229 reporter = InfluxReporter( 230 registry=registry, 231 reporting_interval=10, 232 database=rest_api_config.opentsdb_db, 233 prefix="sawtooth_rest_api", 234 port=db_port, 235 protocol=proto, 236 server=db_server, 237 username=rest_api_config.opentsdb_username, 238 password=rest_api_config.opentsdb_password) 239 reporter.start() 240 241 start_rest_api( 242 host, 243 port, 244 connection, 245 int(rest_api_config.timeout), 246 wrapped_registry, 247 client_max_size=rest_api_config.client_max_size) 248 # pylint: disable=broad-except 249 except Exception as e: 250 LOGGER.exception(e) 251 sys.exit(1) 252 finally: 253 if connection is not None: 254 connection.close()