Skip to content

Commit

Permalink
Merge pull request #479 from zhuyuanmao/download-node-config
Browse files Browse the repository at this point in the history
Add get channel config endpoint
  • Loading branch information
yeasy authored Aug 11, 2022
2 parents c9f538b + a77cb13 commit 6354fd4
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 25 deletions.
94 changes: 94 additions & 0 deletions bootup/docker-compose-files/docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# This compose file will deploy the services, and bootup a postgres server.

#
# SPDX-License-Identifier: Apache-2.0
#

# cello-dashboard: dashboard service for cello
# cello-api-engine: api engine service of cello to provide RESTful APIs, listen on 8080
# cello-postgres: postgres db

version: '3.2'
services:
cello-dashboard:
image: hyperledger/cello-dashboard
container_name: cello-dashboard
ports:
- "${DASHBOARD_SERVICE_PORT}:8081"
networks:
- cello-net

# pg database
cello-postgres:
image: postgres:11.1
container_name: cello-postgres
restart: unless-stopped
environment:
- POSTGRES_DB=api_engine
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=123456
ports:
- "5432:5432"
volumes:
- /opt/cello/pgdata:/var/lib/postgresql/data
networks:
- cello-net

cello-redis:
image: redis:4.0.13
hostname: cello-redis
container_name: cello-redis
volumes:
- /opt/cello/redis:/data

# api engine service of cello
cello-api-engine:
image: hyperledger/cello-api-engine
container_name: cello-api-engine
restart: unless-stopped
stdin_open: true
dns_search: .
environment:
- GODEBUG=netdns=go
- DB_NAME=api_engine
- DB_USER=postgres
- DB_PASSWORD=123456
- DB_HOST=cello-postgres
- CELERY_BROKER_URL=redis://redis
- DB_PORT=5432
- DEBUG=True
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- FABRIC_LOGGING_SPEC=INFO
ports:
- "8080:8080"
volumes:
- /opt/cello:/opt/cello
networks:
- cello-net

# cello docker agent may deploy to an individual server. This cofig only serves development purpose.
cello-docker-agent:
image: hyperledger/cello-agent-docker
container_name: cello-docker-agent
hostname: cello.docker.agent
restart: always
ports:
- "2375:2375"
- "5001:5001"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DOCKER_URL=unix://var/run/docker.sock
- STORAGE_PATH=/opt/hyperledger
networks:
- cello-net

networks:
cello-net:
name: cello-net

volumes:
cello-api-engine:
cello-postgres:
cello-dashboard:
cello-docker-agent:
42 changes: 24 additions & 18 deletions src/api-engine/api/lib/configtxlator/configtxlator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#
# SPDX-License-Identifier: Apache-2.0
#
from subprocess import call
from subprocess import call, run
from api.config import FABRIC_TOOL


Expand All @@ -10,9 +10,8 @@ class ConfigTxLator:
Class represents configtxlator CLI.
"""

def __init__(self, filepath="", configtxlator=FABRIC_TOOL, version="2.2.0"):
def __init__(self, configtxlator=FABRIC_TOOL, version="2.2.0"):
self.configtxlator = configtxlator + "/configtxlator"
self.filepath = filepath
self.version = version

def proto_encode(self, input, type, output):
Expand All @@ -25,16 +24,17 @@ def proto_encode(self, input, type, output):
output: A file to write the output to.
"""
try:
call([self.configtxlator,
"--input", "{}/{}".format(self.filepath, input),
"--type", type,
"--output", "{}/{}".format(self.filepath, output),
])
res = call([self.configtxlator,
"proto_encode",
"--input={}".format(input),
"--type={}".format(type),
"--output={}".format(output),
])
except Exception as e:
err_msg = "configtxlator proto decode fail! "
raise Exception(err_msg + str(e))

def proto_decode(self, input, type, output):
def proto_decode(self, input, type):
"""
Converts a proto message to JSON.
Expand All @@ -44,11 +44,16 @@ def proto_decode(self, input, type, output):
output: A file to write the output to.
"""
try:
call([self.configtxlator,
"--input", "{}/{}".format(self.filepath, input),
"--type", type,
"--output", "{}/{}".format(self.filepath, output),
])
res = run([self.configtxlator,
"proto_decode",
"--type={}".format(type),
"--input={}".format(input),
],
capture_output= True)
if res.returncode == 0 :
return res.stdout
else:
return res.stderr
except Exception as e:
err_msg = "configtxlator proto decode fail! "
raise Exception(err_msg + str(e))
Expand All @@ -66,10 +71,11 @@ def compute_update(self, original, updated, channel_id, output):
"""
try:
call([self.configtxlator,
"--original", original,
"--updated", updated,
"--channel_id", channel_id,
"--output", "{}/{}".format(self.filepath, output)
"compute_update",
"--original={}".format(original),
"--updated={}".format(updated),
"--channel_id={}".format(channel_id),
"--output={}".format(output),
])
except Exception as e:
err_msg = "configtxlator compute update fail! "
Expand Down
14 changes: 9 additions & 5 deletions src/api-engine/api/lib/peer/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,21 +70,25 @@ def update(self, channel, channel_tx, orderer_url):
res = res >> 8
return res

def fetch(self, option, channel, orderer_url, time_out="90s"):
def fetch(self, option, channel):
"""
Fetch a specified block, writing it to a file e.g. <channelID>.block.
params:
option: block option newest|oldest|config|(block number).
channel: channel id.
orderer_url: Ordering service endpoint.
"""
try:
res = os.system("{} channel fetch {} -c {} -o {} --timeout {}".format(
self.peer, option, channel, orderer_url, time_out))
res = subprocess.call(args=[
self.peer,
"channel",
"fetch",
"{}".format(option),
"-c",
channel
])
except Exception as e:
err_msg = "fetch a specified block failed {}!".format(e)
raise Exception(err_msg)
res = res >> 8
return res

def signconfigtx(self, channel_tx):
Expand Down
3 changes: 3 additions & 0 deletions src/api-engine/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
FabricVersions,
)
from api.utils.common import make_uuid, random_name, hash_file
from api.config import CELLO_HOME

SUPER_USER_TOKEN = getattr(settings, "ADMIN_TOKEN", "")
MAX_CAPACITY = getattr(settings, "MAX_AGENT_CAPACITY", 100)
Expand Down Expand Up @@ -750,6 +751,8 @@ class Channel(models.Model):
help_text="Orderer list in the channel",
)

def get_channel_config_path(self):
return "/var/www/server/" + self.name +"_config.block"
# class ChainCode(models.Model):
# id = models.UUIDField(
# primary_key=True,
Expand Down
38 changes: 36 additions & 2 deletions src/api-engine/api/routes/channel/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
#
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

Expand All @@ -12,10 +13,11 @@

from api.config import CELLO_HOME
from api.common.serializers import PageQuerySerializer
from api.utils.common import with_common_response
from api.utils.common import with_common_response, to_dict
from api.auth import TokenAuth
from api.lib.configtxgen import ConfigTX, ConfigTxGen
from api.lib.peer.channel import Channel as PeerChannel
from api.lib.configtxlator.configtxlator import ConfigTxLator
from api.exceptions import (
ResourceNotFound,
)
Expand All @@ -31,6 +33,10 @@
ChannelUpdateSerializer
)
from api.common import ok, err
from api.common.enums import (
NodeStatus,
FabricNodeType,
)


class ChannelViewSet(viewsets.ViewSet):
Expand Down Expand Up @@ -188,7 +194,35 @@ def update(self, request, pk=None):
return Response(status=status.HTTP_202_ACCEPTED)
except ObjectDoesNotExist:
raise ResourceNotFound

@swagger_auto_schema(
responses=with_common_response({status.HTTP_200_OK: "Accepted"}),
)
@action(
methods=["get"],
detail=True,
url_path="config"
)
def get_channel_org_config(self, request, pk=None):
try:
org = request.user.organization
channel = Channel.objects.get(id=pk)
path = channel.get_channel_config_path()
node = Node.objects.filter(
organization=org,
type=FabricNodeType.Peer.name.lower(),
status=NodeStatus.Running.name.lower()
).first()
dir_node = "{}/{}/crypto-config/peerOrganizations".format(
CELLO_HOME, org.name)
env = {
"FABRIC_CFG_PATH": "{}/{}/peers/{}/".format(dir_node, org.name, node.name + "." + org.name),
}
peer_channel_cli = PeerChannel("v2.2.0", **env)
peer_channel_cli.fetch(option="config", channel=channel.name)
config = ConfigTxLator().proto_decode(input=path,type="common.Block")
except ObjectDoesNotExist:
raise ResourceNotFound
return Response(data=to_dict(config, org.name.split(".")[0].capitalize()),status=status.HTTP_200_OK)

def init_env_vars(node, org):
"""
Expand Down
19 changes: 19 additions & 0 deletions src/api-engine/api/utils/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from api.common.serializers import BadResponseSerializer
import uuid
from zipfile import ZipFile
from json import loads


def make_uuid():
Expand Down Expand Up @@ -129,3 +130,21 @@ def zip_file(dirpath, outFullName):
zfile = ZipFile(outFullName, "w")
zfile.write(dirpath, dirpath.rsplit("/", 1)[1])
zfile.close()

def to_dict(data, org_name):
"""
Parse org config from channel config block.
:param data: channel config block in json format.
:param org_name: the organization prefix name
:return organization config
"""
config = loads(data)
if config.get("data") != None:
payloads = config["data"]["data"]
for p in payloads:
groups = p["payload"]["data"]["config"]["channel_group"]["groups"]["Application"]["groups"]
res = groups.get(org_name, None)
if res != None:
return res
return {"error": "can't find channel config"}

0 comments on commit 6354fd4

Please sign in to comment.