diff --git a/src/api-engine/api/lib/configtxgen/configtx.py b/src/api-engine/api/lib/configtxgen/configtx.py index 945d39458..83fd98851 100644 --- a/src/api-engine/api/lib/configtxgen/configtx.py +++ b/src/api-engine/api/lib/configtxgen/configtx.py @@ -51,13 +51,13 @@ def create(self, name, consensus, orderers, peers, orderer_cfg=None, application Consenters = [] for orderer in orderers: - OrdererMSP = orderer["name"].capitalize() + "Orderer" - OrdererOrg = dict(Name=orderer["name"].split(".")[0].capitalize() + "Orderer", - ID='{}MSP'.format(OrdererMSP), + OrdererMSP = "OrdererMSP" + OrdererOrg = dict(Name="Orderer", + ID= OrdererMSP, MSPDir='{}/{}/crypto-config/ordererOrganizations/{}/msp'.format(self.filepath, orderer["name"], orderer['name'].split(".", 1)[1]), - Policies=dict(Readers=dict(Type="Signature", Rule="OR('{}MSP.member')".format(OrdererMSP)), - Writers=dict(Type="Signature", Rule="OR('{}MSP.member')".format(OrdererMSP)), - Admins=dict(Type="Signature", Rule="OR('{}MSP.admin')".format(OrdererMSP))) + Policies=dict(Readers=dict(Type="Signature", Rule="OR('{}.member')".format(OrdererMSP)), + Writers=dict(Type="Signature", Rule="OR('{}.member')".format(OrdererMSP)), + Admins=dict(Type="Signature", Rule="OR('{}.admin')".format(OrdererMSP))) ) for host in orderer['hosts']: OrdererAddress.append('{}.{}:{}'.format(host['name'], orderer['name'].split(".", 1)[1], 7050)) @@ -74,15 +74,14 @@ def create(self, name, consensus, orderers, peers, orderer_cfg=None, application PeerOrganizations = [] for peer in peers: - PeerMSP = peer["name"].capitalize() - PeerOrganizations.append(dict(Name=peer["name"].split(".")[0].capitalize(), - ID='{}MSP'.format(PeerMSP), + PeerMSP = peer['name'].split(".", 1)[0].capitalize() + "MSP" + PeerOrganizations.append(dict(Name=peer['name'].split(".", 1)[0].capitalize(), + ID=PeerMSP, MSPDir='{}/{}/crypto-config/peerOrganizations/{}/msp'.format(self.filepath, peer['name'], peer['name']), - # AnchorPeers=[{'Port': peer["hosts"][0]["port"], 'Host': '{}.{}'.format(peer["hosts"][0]["name"],peer["name"])}], - Policies=dict(Readers=dict(Type="Signature", Rule="OR('{}MSP.member')".format(PeerMSP)), - Writers=dict(Type="Signature", Rule="OR('{}MSP.member')".format(PeerMSP)), - Admins=dict(Type="Signature", Rule="OR('{}MSP.admin')".format(PeerMSP)), - Endorsement=dict(Type="Signature", Rule="OR('{}MSP.member')".format(PeerMSP))) + Policies=dict(Readers=dict(Type="Signature", Rule="OR('{}.admin', '{}.peer', '{}.client')".format(PeerMSP, PeerMSP, PeerMSP)), + Writers=dict(Type="Signature", Rule="OR('{}.admin', '{}.client')".format(PeerMSP, PeerMSP)), + Admins=dict(Type="Signature", Rule="OR('{}.admin')".format(PeerMSP)), + Endorsement=dict(Type="Signature", Rule="OR('{}.peer')".format(PeerMSP))) )) Organizations = OrdererOrganizations + PeerOrganizations Capabilities = dict( diff --git a/src/api-engine/api/lib/configtxgen/configtxgen.py b/src/api-engine/api/lib/configtxgen/configtxgen.py index 9ee1111ad..c46c4257c 100644 --- a/src/api-engine/api/lib/configtxgen/configtxgen.py +++ b/src/api-engine/api/lib/configtxgen/configtxgen.py @@ -1,9 +1,9 @@ # # SPDX-License-Identifier: Apache-2.0 # -from subprocess import call from api.config import CELLO_HOME, FABRIC_TOOL, FABRIC_VERSION +import subprocess import logging LOG = logging.getLogger(__name__) @@ -42,9 +42,13 @@ def genesis(self, profile="", channelid="", outputblock="genesis.block"): "-channelID", "{}".format(channelid) ] - LOG.info("Running command: " + " ".join(command)) + LOG.info(" ".join(command)) - call(command) + subprocess.run(command, check=True) + + except subprocess.CalledProcessError as e: + err_msg = "configtxgen genesis fail! " + raise Exception(err_msg+str(e)) except Exception as e: err_msg = "configtxgen genesis fail! " diff --git a/src/api-engine/api/lib/peer/channel.py b/src/api-engine/api/lib/peer/channel.py index 641b2a9f9..e3b000a3b 100644 --- a/src/api-engine/api/lib/peer/channel.py +++ b/src/api-engine/api/lib/peer/channel.py @@ -4,6 +4,7 @@ import os import json import subprocess +import time from api.lib.peer.command import Command from api.config import FABRIC_TOOL, FABRIC_VERSION import logging @@ -20,23 +21,39 @@ def __init__(self, version=FABRIC_VERSION, peer=FABRIC_TOOL, **kwargs): def create(self, channel, orderer_admin_url, block_path, time_out="90s"): try: - res = 0x100 - command = "" + command = [] if os.getenv("CORE_PEER_TLS_ENABLED") == "false" or os.getenv("CORE_PEER_TLS_ENABLED") is None: - command = "{} channel join --channelID {} --config-block {} -o {}".format(self.osnadmin, channel, block_path, orderer_admin_url) + command = [ + self.osnadmin, + "channel", "join", + "--channelID", channel, + "--config-block", block_path, + "-o", orderer_admin_url, + ] else: ORDERER_CA = os.getenv("ORDERER_CA") ORDERER_ADMIN_TLS_SIGN_CERT = os.getenv("ORDERER_ADMIN_TLS_SIGN_CERT") ORDERER_ADMIN_TLS_PRIVATE_KEY = os.getenv("ORDERER_ADMIN_TLS_PRIVATE_KEY") - command = "{} channel join --channelID {} --config-block {} -o {} --ca-file {} --client-cert {} --client-key {}".format(self.osnadmin, channel, block_path, orderer_admin_url, ORDERER_CA, ORDERER_ADMIN_TLS_SIGN_CERT, ORDERER_ADMIN_TLS_PRIVATE_KEY) - - LOG.info(f"{command}") - res = os.system(command) - - # The return value of os.system is not the result of executing the program. It is a 16 bit number, - # and its high bit is the return code - res = res >> 8 + command = [ + self.osnadmin, + "channel", "join", + "--channelID", channel, + "--config-block", block_path, + "-o", orderer_admin_url, + "--ca-file", ORDERER_CA, + "--client-cert", ORDERER_ADMIN_TLS_SIGN_CERT, + "--client-key", ORDERER_ADMIN_TLS_PRIVATE_KEY + ] + + LOG.info(" ".join(command)) + + res = subprocess.run(command, check=True) + + except subprocess.CalledProcessError as e: + err_msg = "create channel failed for {}!".format(e) + raise Exception(err_msg+str(e)) + except Exception as e: err_msg = "create channel failed for {}!".format(e) raise Exception(err_msg) @@ -78,30 +95,58 @@ def update(self, channel, channel_tx, orderer_url): res = res >> 8 return res - def fetch(self, block_path, channel, orderer_general_url): + def fetch(self, block_path, channel, orderer_general_url, max_retries=5, retry_interval=1): """ Fetch a specified block, writing it to a file e.g. .block. params: option: block option newest|oldest|config|(block number). channel: channel id. """ - try: - res = 0x100 - command = "" - if os.getenv("CORE_PEER_TLS_ENABLED") == "false" or os.getenv("CORE_PEER_TLS_ENABLED") is None: - command = "{} channel fetch config {} -o {} -c {}".format(self.peer, block_path, orderer_general_url, channel) - else: - ORDERER_CA = os.getenv("ORDERER_CA") - orderer_address = orderer_general_url.split(":")[0] - command = "{} channel fetch config {} -o {} --ordererTLSHostnameOverride {} -c {} --tls --cafile {}".format(self.peer, block_path, orderer_general_url, orderer_address, channel, ORDERER_CA) + res = 0 + command = [] + if os.getenv("CORE_PEER_TLS_ENABLED") == "false" or os.getenv("CORE_PEER_TLS_ENABLED") is None: + command = [ + self.peer, + "channel", "fetch", + "config", block_path, + "-o", orderer_general_url, + "-c", channel + ] + else: + ORDERER_CA = os.getenv("ORDERER_CA") + orderer_address = orderer_general_url.split(":")[0] + command = [ + self.peer, + "channel", "fetch", + "config", block_path, + "-o", orderer_general_url, + "--ordererTLSHostnameOverride", orderer_address, + "-c", channel, + "--tls", + "--cafile", ORDERER_CA + ] + + LOG.info(" ".join(command)) + + # Retry fetching the block up to max_retries times + for attempt in range(1, max_retries+1): + try: + LOG.debug("Attempt %d/%d to fetch block", attempt, max_retries) + + res = subprocess.run(command, check=True) + + LOG.info("Successfully fetched block") + break + + except subprocess.CalledProcessError as e: + LOG.debug(f"Attempt {attempt}/{max_retries} failed") + + if attempt <= max_retries: + time.sleep(retry_interval) + else: + LOG.error(f"Failed to fetch block after {max_retries} attempts") + raise e - LOG.info(f"{command}") - res = os.system(command) - - res = res >> 8 - except Exception as e: - err_msg = "fetch a specified block failed {}!".format(e) - raise Exception(err_msg) return res def signconfigtx(self, channel_tx): diff --git a/src/api-engine/api/lib/pki/cryptogen/cryptocfg.py b/src/api-engine/api/lib/pki/cryptogen/cryptocfg.py index 36c002eba..c9eff0dcd 100644 --- a/src/api-engine/api/lib/pki/cryptogen/cryptocfg.py +++ b/src/api-engine/api/lib/pki/cryptogen/cryptocfg.py @@ -59,7 +59,7 @@ def create(self, peernum, orderernum) -> None: else: template = dict(Count=orderernum) org.append(dict(Domain=self.name.split(".", 1)[1], - Name=self.name.split(".")[0].capitalize() + item, + Name=item, CA=ca, Specs=specs, EnableNodeOUs=self.enablenodeous, @@ -89,6 +89,7 @@ def update(self, org_info: any) -> None: orgs = network['OrdererOrgs'] for org in orgs: + # org["Template"]["Count"] += 1 specs = org["Specs"] for host in org_info["Specs"]: specs.append(dict(Hostname=host)) diff --git a/src/api-engine/api/lib/pki/cryptogen/cryptogen.py b/src/api-engine/api/lib/pki/cryptogen/cryptogen.py index b27baddf0..bad3fb466 100644 --- a/src/api-engine/api/lib/pki/cryptogen/cryptogen.py +++ b/src/api-engine/api/lib/pki/cryptogen/cryptogen.py @@ -40,7 +40,7 @@ def generate(self, output="crypto-config", config="crypto-config.yaml"): "--config={}/{}/{}".format(self.filepath, self.name, config) ] - LOG.info("Running command: " + " ".join(command)) + LOG.info(" ".join(command)) call(command) @@ -63,7 +63,7 @@ def extend(self, input="crypto-config", config="crypto-config.yaml"): "--config={}/{}/{}".format(self.filepath, self.name, config) ] - LOG.info("Running command: " + " ".join(command)) + LOG.info(" ".join(command)) call(command) diff --git a/src/api-engine/api/routes/channel/views.py b/src/api-engine/api/routes/channel/views.py index 633afe005..65d4ed811 100644 --- a/src/api-engine/api/routes/channel/views.py +++ b/src/api-engine/api/routes/channel/views.py @@ -126,57 +126,30 @@ def create(self, request): try: org = request.user.organization - # Check if nodes are running - for i in range(len(orderers)): - o = Node.objects.get(id=orderers[i]) - if o.status != "running": - raise NoResource - for i in range(len(peers)): - p = Node.objects.get(id=peers[i]) - if p.status != "running": - raise NoResource - - _orderers = [] - _peers = [] - _orderers.append({"name": org.name, "hosts": []}) - _peers.append({"name": org.name, "hosts": []}) - nodes = Node.objects.filter(organization=org) - for node in nodes: - if node.type == "peer": - _peers[0]["hosts"].append({"name": node.name}) - elif node.type == "orderer": - _orderers[0]["hosts"].append({"name": node.name}) + orderer_nodes = Node.objects.filter(id__in=orderers) + peer_nodes = Node.objects.filter(id__in=peers) + + # validate if all nodes are running + validate_nodes(orderer_nodes) + validate_nodes(peer_nodes) + + # assemble transaction config + _orderers, _peers = assemble_transaction_config(org) ConfigTX(org.network.name).create(name, org.network.consensus, _orderers, _peers) ConfigTxGen(org.network.name).genesis(profile=name, channelid=name, outputblock="{}.block".format(name)) # osnadmin channel join ordering_node = Node.objects.get(id=orderers[0]) - envs = init_env_vars(ordering_node, org) - peer_channel_cli = PeerChannel(**envs) - peer_channel_cli.create( - channel=name, - orderer_admin_url="{}.{}:{}".format( - ordering_node.name, org.name.split(".", 1)[1], str(7053)), - block_path="{}/{}/{}.block".format( - CELLO_HOME, org.network.name, name) - ) + osn_channel_join(name, ordering_node, org) # peer channel join - for i in range(len(peers)): - peer_node = Node.objects.get(id=peers[i]) - envs = init_env_vars(peer_node, org) - join_peers(envs, "{}/{}/{}.block".format( - CELLO_HOME, org.network.name, name)) - - # peer channel fetch - peer_node = Node.objects.get(id=peers[0]) - envs = init_env_vars(peer_node, org) - peer_channel_cli = PeerChannel(**envs) - peer_channel_cli.fetch(block_path="{}/{}/config_block.pb".format(CELLO_HOME, org.network.name), - channel=name, orderer_general_url="{}.{}:{}".format( - ordering_node.name, org.name.split(".", 1)[1], str(7050))) + peer_channel_join(name, peers, org) + + # set anchor peer + set_anchor_peer(name, org, peers, ordering_node) + # save channel to db channel = Channel( name=name, network=org.network @@ -184,6 +157,8 @@ def create(self, request): channel.save() channel.organizations.add(org) channel.orderers.add(ordering_node) + + # serialize and return channel id response = ChannelIDSerializer(data=channel.__dict__) if response.is_valid(raise_exception=True): return Response( @@ -387,6 +362,94 @@ def get_channel_org_config(self, request, pk=None): except ObjectDoesNotExist: raise ResourceNotFound +def validate_nodes(nodes): + """ + validate if all nodes are running + :param nodes: list of nodes + :return: none + """ + for node in nodes: + if node.status != "running": + raise NoResource("Node {} is not running".format(node.name)) + +def assemble_transaction_config(org): + """ + Assemble transaction config for the channel. + :param org: Organization object. + :return: _orderers, _peers + """ + _orderers = [{"name": org.name, "hosts": []}] + _peers = [{"name": org.name, "hosts": []}] + nodes = Node.objects.filter(organization=org) + for node in nodes: + if node.type == "peer": + _peers[0]["hosts"].append({"name": node.name}) + elif node.type == "orderer": + _orderers[0]["hosts"].append({"name": node.name}) + + return _orderers, _peers + + +def osn_channel_join(name, ordering_node, org): + """ + Join ordering node to the channel. + :param ordering_node: Node object + :param org: Organization object. + :param channel_name: Name of the channel. + :return: none + """ + envs = init_env_vars(ordering_node, org) + peer_channel_cli = PeerChannel(**envs) + peer_channel_cli.create( + channel=name, + orderer_admin_url="{}.{}:{}".format( + ordering_node.name, org.name.split(".", 1)[1], str(7053)), + block_path="{}/{}/{}.block".format( + CELLO_HOME, org.network.name, name) + ) + +def peer_channel_join(name, peers, org): + """ + Join peer nodes to the channel. + :param peers: list of Node objects + :param org: Organization object. + :param channel_name: Name of the channel. + :return: none + """ + for i in range(len(peers)): + peer_node = Node.objects.get(id=peers[i]) + envs = init_env_vars(peer_node, org) + peer_channel_cli = PeerChannel(**envs) + peer_channel_cli.join( + block_path="{}/{}/{}.block".format( + CELLO_HOME, org.network.name, name) + ) + +def set_anchor_peer(name, org, peers, ordering_node): + """ + Set anchor peer for the channel. + :param org: Organization object. + :param peers: list of Node objects + :return: none + """ + peer_channel_fetch(name, org, peers, ordering_node) + + +def peer_channel_fetch(name, org, peers, ordering_node): + """ + Fetch the channel block from the orderer. + :param peers: list of Node objects + :param org: Organization object. + :param channel_name: Name of the channel. + :return: none + """ + peer_node = Node.objects.get(id=peers[0]) + envs = init_env_vars(peer_node, org) + peer_channel_cli = PeerChannel(**envs) + peer_channel_cli.fetch(block_path="{}/{}/config_block.pb".format(CELLO_HOME, org.network.name), + channel=name, orderer_general_url="{}.{}:{}".format( + ordering_node.name, org.name.split(".", 1)[1], str(7050))) + def init_env_vars(node, org): """ @@ -414,26 +477,12 @@ def init_env_vars(node, org): elif(node.type == "peer"): envs = { "CORE_PEER_TLS_ENABLED": "true", - "CORE_PEER_LOCALMSPID": "{}MSP".format(org_name.capitalize()), + "CORE_PEER_LOCALMSPID": "{}MSP".format(org_name.split(".")[0].capitalize()), "CORE_PEER_TLS_ROOTCERT_FILE": "{}/{}/peers/{}/tls/ca.crt".format(dir_node, org_name, node.name + "." + org_name), "CORE_PEER_MSPCONFIGPATH": "{}/{}/users/Admin@{}/msp".format(dir_node, org_name, org_name), "CORE_PEER_ADDRESS": "{}:{}".format( node.name + "." + org_name, str(7051)), - "FABRIC_CFG_PATH": "{}/{}/peers/{}/".format(dir_node, org_name, node.name + "." + org_name) } return envs - - -def join_peers(envs, block_path): - """ - Join peer nodes to the channel. - :param envs: environments variables for peer CLI. - :param block_path: Path to file containing genesis block - """ - # Join the peers to the channel. - peer_channel_cli = PeerChannel(**envs) - peer_channel_cli.join( - block_path=block_path - )