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