github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/bddtests/steps/docgen.py (about)

     1  # Copyright IBM Corp. 2016 All Rights Reserved.
     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  from StringIO import StringIO
    17  from itertools import chain
    18  from google.protobuf.message import Message
    19  
    20  from b3j0f.aop import weave, unweave, is_intercepted, weave_on
    21  
    22  from jinja2 import Environment, PackageLoader, select_autoescape, FileSystemLoader, Template
    23  env = Environment(
    24      loader=FileSystemLoader(searchpath="templates"),
    25      autoescape=select_autoescape(['html', 'xml']),
    26      trim_blocks=True,
    27      lstrip_blocks=True
    28  )
    29  
    30  from bootstrap_util import getDirectory
    31  
    32  class DocumentGenerator:
    33  
    34  
    35      def __init__(self, contextHelper, scenario):
    36          self.contextHelper = contextHelper
    37          self.directory = getDirectory(contextHelper.context)
    38          self.output = StringIO()
    39          self.currentStep = 0
    40          self.composition = None
    41  
    42          #Weave advices into contextHelper
    43          weave(target=self.contextHelper.before_step, advices=self.beforeStepAdvice)
    44          weave(target=self.contextHelper.after_step, advices=self.afterStepAdvice)
    45          weave(target=self.contextHelper.after_scenario, advices=self.afterScenarioAdvice)
    46          weave(target=self.contextHelper.getBootrapHelper, advices=self.getBootstrapHelperAdvice)
    47          weave(target=self.contextHelper.registerComposition, advices=self.registerCompositionAdvice)
    48  
    49          # Weave advices into Directory
    50          weave(target=self.directory._registerOrg, advices=self.registerOrgAdvice)
    51          weave(target=self.directory._registerUser, advices=self.registerUserAdvice)
    52          weave(target=self.directory.registerOrdererAdminTuple, advices=self.registerNamedNodeAdminTupleAdvice)
    53  
    54      def beforeStepAdvice(self, joinpoint):
    55          self.currentStep += 1
    56          step = joinpoint.kwargs['step']
    57          # Now the jinja template
    58          self.output.write(env.get_template("html/step.html").render(step_id="Step {0}".format(self.currentStep), step=step))
    59          return joinpoint.proceed()
    60  
    61      def afterStepAdvice(self, joinpoint):
    62          step = joinpoint.kwargs['step']
    63          # Now the jinja template
    64          if step.status=="failed":
    65              self.output.write(env.get_template("html/error.html").render(err=step.error_message))
    66          return joinpoint.proceed()
    67  
    68  
    69      def compositionCallCLIAdvice(self, joinpoint):
    70          'This advice is called around the compositions usage of the cli'
    71          result = joinpoint.proceed()
    72          # Create table for environment
    73          composition = joinpoint.kwargs['self']
    74          envAdditions = composition.getEnvAdditions()
    75          keys = envAdditions.keys()
    76          keys.sort()
    77          envPreamble = " ".join(["{0}={1}".format(key,envAdditions[key]) for key in keys])
    78          args= " ".join(joinpoint.kwargs['argList'])
    79          self.output.write(env.get_template("html/cli.html").render(command="{0} {1}".format(envPreamble, args)))
    80          return result
    81  
    82      def _getNetworkGroup(self, serviceName):
    83          groups = {"peer" : 1, "orderer" : 2, "kafka" : 7, "zookeeper" : 8, "couchdb" : 9}
    84          groupId = 0
    85          for group, id in groups.iteritems():
    86              if serviceName.lower().startswith(group):
    87                  groupId = id
    88          return groupId
    89  
    90      def _getNetworkForConfig(self, configAsYaml):
    91          import yaml
    92          config = yaml.load(configAsYaml)
    93          assert "services" in config, "Expected config from docker-compose config to have services key at top level:  \n{0}".format(config)
    94          network = {"nodes": [], "links" : []}
    95          for serviceName in config['services'].keys():
    96              network['nodes'].append({"id" : serviceName, "group" : self._getNetworkGroup(serviceName), "type" : "node"})
    97              # Now get links
    98              if "depends_on" in config['services'][serviceName]:
    99                  for dependedOnServiceName in config['services'][serviceName]['depends_on']:
   100                      network['links'].append({"source": serviceName, "target": dependedOnServiceName, "value" : 1})
   101          return network
   102  
   103      def _getNetworkForDirectory(self):
   104          network = {"nodes":[], "links": []}
   105          for orgName, org in self.directory.getOrganizations().iteritems():
   106              network['nodes'].append({"id" : orgName, "group" : 3, "type" : "org"})
   107          for userName, user in self.directory.getUsers().iteritems():
   108              network['nodes'].append({"id" : userName, "group" : 4, "type" : "user"})
   109          # Now get links
   110          for nct, cert in self.directory.getNamedCtxTuples().iteritems():
   111              nctId = "{0}-{1}-{2}".format(nct.user, nct.nodeName, nct.organization)
   112              network['nodes'].append({"id" : nctId, "group" : 5, "type" : "cert"})
   113              network['links'].append({"source": nctId, "target": nct.organization, "value" : 1})
   114              network['links'].append({"source": nctId, "target": nct.user, "value" : 1})
   115              # Only add the context link if it is a compose service, else the target may not exist.
   116              if nct.nodeName in self.composition.getServiceNames():
   117                  network['links'].append({"source": nctId, "target": nct.nodeName, "value" : 1})
   118          return network
   119  
   120      def _writeNetworkJson(self):
   121          if self.composition:
   122              import json
   123              configNetwork = self._getNetworkForConfig(configAsYaml=self.composition.getConfig())
   124              directoryNetwork = self._getNetworkForDirectory()
   125              # Join the network info together
   126              fullNetwork = dict(chain([(key, configNetwork[key] + directoryNetwork[key]) for key in configNetwork.keys()]))
   127              (fileName, fileExists) = self.contextHelper.getTmpPathForName("network", extension="json")
   128              with open(fileName, "w") as f:
   129                  f.write(json.dumps(fullNetwork))
   130  
   131  
   132      def registerCompositionAdvice(self, joinpoint):
   133          composition = joinpoint.kwargs['composition']
   134          weave(target=composition._callCLI, advices=self.compositionCallCLIAdvice)
   135          result = joinpoint.proceed()
   136          if composition:
   137              #Now get the config for the composition and dump out.
   138              self.composition = composition
   139              configAsYaml = composition.getConfig()
   140              self.output.write(env.get_template("html/header.html").render(text="Configuration", level=4))
   141              self.output.write(env.get_template("html/cli.html").render(command=configAsYaml))
   142              #Inject the graph
   143              self.output.write(env.get_template("html/header.html").render(text="Network Graph", level=4))
   144              self.output.write(env.get_template("html/graph.html").render())
   145          return result
   146  
   147      def _addLinkToFile(self, fileName ,linkText):
   148          import ntpath
   149          baseName = ntpath.basename(fileName)
   150          # self.markdownWriter.addLink(linkUrl="./{0}".format(baseName), linkText=linkText, linkTitle=baseName)
   151  
   152      def _getLinkInfoForFile(self, fileName):
   153          import ntpath
   154          return "./{0}".format(ntpath.basename(fileName))
   155  
   156      def registerOrgAdvice(self, joinpoint):
   157          orgName = joinpoint.kwargs['orgName']
   158          newlyRegisteredOrg = joinpoint.proceed()
   159          orgCert = newlyRegisteredOrg.getCertAsPEM()
   160          #Write out key material
   161          (fileName, fileExists) = self.contextHelper.getTmpPathForName(name="dir-org-{0}-cert".format(orgName), extension="pem")
   162          with open(fileName, 'w') as f:
   163              f.write(orgCert)
   164          self._addLinkToFile(fileName=fileName, linkText="Public cert for Organization")
   165          #Now the jinja output
   166          self.output.write(env.get_template("html/org.html").render(org=newlyRegisteredOrg, cert_href=self._getLinkInfoForFile(fileName), path_to_cert=fileName))
   167          return newlyRegisteredOrg
   168  
   169      def registerUserAdvice(self, joinpoint):
   170          userName = joinpoint.kwargs['userName']
   171          newlyRegisteredUser = joinpoint.proceed()
   172          #Write out key material
   173          privateKeyAsPem = newlyRegisteredUser.getPrivateKeyAsPEM()
   174          (fileName, fileExists) = self.contextHelper.getTmpPathForName(name="dir-user-{0}-privatekey".format(userName), extension="pem")
   175          with open(fileName, 'w') as f:
   176              f.write(privateKeyAsPem)
   177          #Weave into user tags setting
   178          weave(target=newlyRegisteredUser.setTagValue, advices=self.userSetTagValueAdvice)
   179          #Now the jinja output
   180          self.output.write(env.get_template("html/user.html").render(user=newlyRegisteredUser, private_key_href=self._getLinkInfoForFile(fileName)))
   181          return newlyRegisteredUser
   182  
   183      def afterScenarioAdvice(self, joinpoint):
   184          scenario = joinpoint.kwargs['scenario']
   185          #Render with jinja
   186          header = env.get_template("html/scenario.html").render(scenario=scenario, steps=scenario.steps)
   187          main = env.get_template("html/main.html").render(header=header, body=self.output.getvalue())
   188          (fileName, fileExists) = self.contextHelper.getTmpPathForName("scenario", extension="html")
   189          with open(fileName, 'w') as f:
   190              f.write(main.encode("utf-8"))
   191          self._writeNetworkJson()
   192          return joinpoint.proceed()
   193  
   194      def registerNamedNodeAdminTupleAdvice(self, joinpoint):
   195          namedNodeAdminTuple = joinpoint.proceed()
   196          directory = joinpoint.kwargs['self']
   197          #jinja
   198          newCertAsPEM = directory.getCertAsPEM(namedNodeAdminTuple)
   199          self.output.write(env.get_template("html/header.html").render(text="Created new named node admin tuple: {0}".format(namedNodeAdminTuple), level=4))
   200          self.output.write(env.get_template("html/cli.html").render(command=newCertAsPEM))
   201          #Write cert out
   202          fileNameTocheck = "dir-user-{0}-cert-{1}-{2}".format(namedNodeAdminTuple.user, namedNodeAdminTuple.nodeName, namedNodeAdminTuple.organization)
   203          (fileName, fileExists) = self.contextHelper.getTmpPathForName(fileNameTocheck, extension="pem")
   204          with open(fileName, 'w') as f:
   205              f.write(newCertAsPEM)
   206          return namedNodeAdminTuple
   207  
   208      def bootstrapHelperSignConfigItemAdvice(self, joinpoint):
   209          configItem = joinpoint.kwargs['configItem']
   210          #jinja
   211          self.output.write(env.get_template("html/header.html").render(text="Dumping signed config item...", level=4))
   212          self.output.write(env.get_template("html/protobuf.html").render(msg=configItem, msgLength=len(str(configItem))))
   213  
   214          signedConfigItem = joinpoint.proceed()
   215          return signedConfigItem
   216  
   217      def getBootstrapHelperAdvice(self, joinpoint):
   218          bootstrapHelper = joinpoint.proceed()
   219          weave(target=bootstrapHelper.signConfigItem, advices=self.bootstrapHelperSignConfigItemAdvice)
   220          return bootstrapHelper
   221  
   222      def _isProtobufMessage(self, target):
   223          return isinstance(target, Message)
   224  
   225      def _isListOfProtobufMessages(self, target):
   226          result = False
   227          if isinstance(target, list):
   228              messageList = [item for item in target if self._isProtobufMessage(item)]
   229              result = len(messageList) == len(target)
   230          return result
   231  
   232      def _isDictOfProtobufMessages(self, target):
   233          result = False
   234          if isinstance(target, dict):
   235              messageList = [item for item in target.values() if self._isProtobufMessage(item)]
   236              result = len(messageList) == len(target)
   237          return result
   238  
   239      def _writeProtobuf(self, fileName, msg):
   240          import ntpath
   241          baseName = ntpath.basename(fileName)
   242          dataToWrite = msg.SerializeToString()
   243          with open("{0}".format(fileName), 'wb') as f:
   244              f.write(dataToWrite)
   245          self.output.write(env.get_template("html/protobuf.html").render(id=baseName, msg=msg, path_to_protobuf=fileName, msgLength=len(dataToWrite),linkUrl="./{0}".format(baseName), linkText="Protobuf message in binary form", linkTitle=baseName))
   246  
   247  
   248      def userSetTagValueAdvice(self, joinpoint):
   249          result = joinpoint.proceed()
   250          user = joinpoint.kwargs['self']
   251          tagKey = joinpoint.kwargs['tagKey']
   252          tagValue = joinpoint.kwargs['tagValue']
   253  
   254          #jinja invoke
   255          self.output.write(env.get_template("html/tag.html").render(user=user, tag_key=tagKey))
   256  
   257          # If protobuf message, write out in binary form
   258          if self._isProtobufMessage(tagValue):
   259              import ntpath
   260              (fileName, fileExists) = self.contextHelper.getTmpPathForName("{0}-{1}".format(user.getUserName(), tagKey), extension="protobuf")
   261              self._writeProtobuf(fileName=fileName, msg=tagValue)
   262          # If protobuf message, write out in binary form
   263          elif self._isListOfProtobufMessages(tagValue):
   264              index = 0
   265              for msg in tagValue:
   266                  (fileName, fileExists) = self.contextHelper.getTmpPathForName("{0}-{1}-{2:0>4}".format(user.getUserName(), tagKey, index), extension="protobuf")
   267                  self._writeProtobuf(fileName=fileName, msg=msg)
   268                  index += 1
   269          elif self._isDictOfProtobufMessages(tagValue):
   270              for key,msg in tagValue.iteritems():
   271                  (fileName, fileExists) = self.contextHelper.getTmpPathForName("{0}-{1}-{2}".format(user.getUserName(), tagKey, key), extension="protobuf")
   272                  self._writeProtobuf(fileName=fileName, msg=msg)
   273          else:
   274              self.output.write(env.get_template("html/cli.html").render(command=str(tagValue)))
   275          return result