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()