fabric链码的编写-入门

2023-10-28

链码的编写

前言:fabric链码的编写较简单,在熟悉了基本结构和相关API之后就可上手编写,但是要多多练习,提高编写链码的速度和正确度。

学习步骤:

1.熟悉链码的基本结构

2.熟练链码相关API

3.练习,练习,练习

参考链接:https://www.cnblogs.com/zongmin/p/11874792.html#_label0_1

1.链码的基本结构

链码的启动必须通过调用shim包中的Start函数,传递一个类型为Chaincode的参数,该参数是一个接口类型,有两个重要的函数InitInvoke函数,(即每个链码都需实现chaincode接口)

Start函数
xiaoliu- Init

在链码实例化或者升级时被调用,完成初始化数据的工作

在此方法中实现链码的初始化或升级时的处理逻辑
xiaoliu

  • Invoke
    更新或查询账本中的数据状态时被调用,需要在此方法中实现响应调用或查询的业务逻辑
    在此方法中实现链码运行中被调用或查询时的处理逻辑
    在这里插入图片描述

在实际开发中,我们可以自行定义一个结构体,实现chaincode接口,重写chaincode接口的两个方法,并将两个方法指定为自定义结构体的成员方法

链码基本结构如下所示:

package main

import (
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)
//自定义结构体
type SimpleChaincode struct {

}
//为结构体添加Init方法
func (s*SimpleChaincode)Init(stub shim.ChaincodeStubInterface)pb.Response  {
	return shim.Success(nil)
}
//为结构体添加Invoke方法
func (s *SimpleChaincode)Invoke(stub shim.ChaincodeStubInterface)pb.Response  {
    return shim.Success(nil)
}
//调用Start()函数
func main()  {
	shim.Start(new(SimpleChaincode))
}

  • shim: 用来访问/操作数据状态、事务上下文和调用其他链代码的 API, 链码通过 shim.ChaincodeStub 提供的方法来读取和修改账本的状态
  • peer: 提供了链码执行后的响应信息的 API,peer.Response 封装了响应信息

2.链码常用API

shim 包提供了如下几种类型的接口:

  • **参数解析 API:**调用链码时需要给被调用的目标函数/方法传递参数,该 API 提供解析这些参数的方法
  • **账本状态数据操作 API:**该 API 提供了对账本数据状态进行操作的方法,包括对状态数据的查询及事务处理等
  • **交易信息获取 API:**获取提交的交易信息的相关 API
  • 对 PrivateData 操作的 API: Hyperledger Fabric 在 1.2.0 版本中新增的对私有数据操作的相关 API
  • **其他 API:**其他的 API,包括事件设置、调用其他链码操作

2.1 参数解析 API

// 返回调用链码时指定提供的参数列表(以字符串数组形式返回)
GetStringArgs() []string

// 返回调用链码时在交易提案中指定提供的被调用的函数名称及函数的参数列表(以字符串数组形式返回)
GetFunctionAndParameters() (function string, params []string)

// 返回提交交易提案时提供的参数列表(以字节串数组形式返回)
GetArgsSlice() ([]byte, error)

// 返回调用链码时在交易提案中指定提供的被调用的函数名称及函数的参数列表(以字符串数组形式返回)
GetArgs() [][]byte

一般使用 GetFunctionAndParameters() 及 GetStringArgs() 。

2.2 账本数据状态操作 API

// 查询账本,返回指定键对应的值
GetState(key string) ([]byte, error)

// 尝试添加/更新账本中的一对键值
// 这一对键值会被添加到写集合中,等待 Committer 进一步确认,验证通过后才会真正写入到账本
PutState(key string, value []byte) error

// 尝试删除账本中的一对键值
// 同样,对该对键值删除会添加到写集合中,等待 Committer 进一步确认,验证通过后才会真正写入到账本
DelState(key string) error

// 查询指定范围的键值,startKey 和 endkey 分别指定开始(包括)和终止(不包括),当为空时默认是最大范围
// 返回结果是一个迭代器结构,可以按照字典序迭代每个键值对,最后需要调用 Close() 方法关闭
GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error)

// 返回指定键的所有历史值。该方法的使用需要节点配置中打开历史数据库特性(ledger.history.enableHistoryDatabase=true)
GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error)

// 给定一组属性(attributes),将这些属性组合起来构造返回一个复合键
// 例如:CreateComositeKey("name-age",[]string{"Alice", "12"});
CreateCompositeKey(objectType string, attributes []string) (string, error)

// 将指定的复合键进行分割,拆分成构造复合键时所用的属性
SplitCompositeKey(compositeKey string) (string, []string, error)

// 根据局部的复合键(前缀)返回所有匹配的键值,即与账本中的键进行前缀匹配
// 返回结果是一个迭代器结构,可以按照字典序迭代每个键值对,最后需要调用 Close() 方法关闭
GetStateByPartialCompositeKey(objectType string, keys []string) (StateQueryIteratorInterface, error)

// 对(支持富查询功能的)状态数据库进行富查询,返回结果是一个迭代器结构,目前只支持 CouchDB
// 注意该方法不会被 Committer 重新执行进行验证,所以不能用于更新账本状态的交易中
GetQueryResult(query string) (StateQueryIteratorInterface, error)

注意: 通过 put 写入的数据状态不能立刻 get 到,因为 put 只是链码执行的模拟交易(防止重复提交攻击),并不会真正将状态保存到账本中,必须经过 Orderer 达成共识之后,将数据状态保存在区块中,然后保存在各 peer 节点的账本中。

2.3 交易信息相关 API[了解即可]

// 返回交易提案中指定的交易 ID。
// 一般情况下,交易 ID 是客户端提交提案时由 Nonce 随机串和签名者身份信息哈希产生的数字摘要
GetTxID() string

// 返回交易提案中指定的 Channel ID
GetChannelID() string

// 返回交易被创建时的客户端打上的的时间戳
// 这个时间戳是直接从交易 ChannnelHeader 中提取的,所以在所以背书节点处看到的值都相同
GetTxTimestamp() (*timestamp.Timestamp, error)

// 返回交易的 binding 信息
// 交易的 binding 信息是将交提案的 nonse、Creator、epoch 等信息组合起来哈希得到数字摘要
GetBinding() ([]byte, error)

// 返回该 stub 的 SignedProposal 结构,包括了跟交易提案相关的所有数据
GetSignedProposal() (*pb.SignedProposal, error)

// 返回该交易提交者的身份信息(用户证书)
// 从 SignedProposal 中的 SignatureHeader.Creator 提取
GetCreator() ([]byte, error)

// 返回交易中带有的一些临时信息
// 从 ChaincodeProposalPayload.transient 提取,可以存放与应用相关的保密信息,该信息不会被写入到账本
GetTransient() (map[string][]byte, error)

GetTransient()在私有数据的编写会用到

2.4 对 PrivateData [私有数据]操作的 API


2.5 其他 API

// 设定当这个交易在 Committer 处被认证通过,写入到区块时发送的事件(event),一般由 Client 监听
SetEvent(name string, payload []byte) error

// 调用另外一个链码的 Invoke 方法
// 如果被调用链码在同一个通道内,则添加其读写集合信息到调用交易;否则执行调用但不影响读写集合信息
// 如果 channel 为空,则默认为当前通道。目前仅限读操作,同时不会生成新的交易
InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response

3.链码开发示例

此链码为fabric-sample/chaincode中的实例链码,可在fabric-sample中查看详情

[要自己跟着手敲一遍!不能光看,多练习!,先自己缕一遍思路,先回想一下链码的基本结构,以及API]

package main

import (
	"fmt"
	"strconv"

	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
)

type SimpleChaincode struct {
}

// 初始化数据状态,实例化/升级链码时被自动调用
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	// println 函数的输出信息会出现在链码容器的日志中
	fmt.Println("ex02 Init")

	// 获取用户传递给调用链码的所需参数
	_, args := stub.GetFunctionAndParameters()

	var A, B string    // 两个账户
	var Aval, Bval int // 两个账户的余额
	var err error

	// 检查合法性, 检查参数数量是否为 4 个, 如果不是, 则返回错误信息
	if len(args) != 4 {
		return shim.Error("Incorrect number of arguments. Expecting 4")
	}

	A = args[0]  // 账户 A 用户名
	Aval, err = strconv.Atoi(args[1])  // 账户 A 余额
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}

	B = args[2]  // 账户 B 用户名
	Bval, err = strconv.Atoi(args[3])  // 账户 B 余额
	if err != nil {
		return shim.Error("Expecting integer value for asset holding")
	}

	fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)

	// 将账户 A 的状态写入账本中
	err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	// 将账户 B 的状态写入账本中
	err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	// 一切成功,返回 nil(shim.Success)
	return shim.Success(nil)
}

// 对账本数据进行操作时(query, invoke)被自动调用
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	fmt.Println("ex02 Invoke")
	// 获取用户传递给调用链码的函数名称及参数
	function, args := stub.GetFunctionAndParameters()

	// 对获取到的函数名称进行判断
	if function == "invoke" {
		// 调用 invoke 函数实现转账操作
		return t.invoke(stub, args)
	} else if function == "delete" {
		// 调用 delete 函数实现账户注销
		return t.delete(stub, args)
	} else if function == "query" {
		// 调用 query 实现账户查询操作
		return t.query(stub, args)
	}
	// 传递的函数名出错,返回 shim.Error()
	return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")
}

// 账户间转钱
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var A, B string    // 账户 A 和 B
	var Aval, Bval int // 账户余额
	var X int          // 转账金额
	var err error

	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 3")
	}

	A = args[0]       // 账户 A 用户名
	B = args[1]       // 账户 B 用户名

	// 从账本中获取 A 的余额
	Avalbytes, err := stub.GetState(A)
	if err != nil {
		return shim.Error("Failed to get state")
	}
	if Avalbytes == nil {
		return shim.Error("Entity not found")
	}
	Aval, _ = strconv.Atoi(string(Avalbytes))

	// 从账本中获取 B 的余额
	Bvalbytes, err := stub.GetState(B)
	if err != nil {
		return shim.Error("Failed to get state")
	}
	if Bvalbytes == nil {
		return shim.Error("Entity not found")
	}
	Bval, _ = strconv.Atoi(string(Bvalbytes))

	// X 为 转账金额
	X, err = strconv.Atoi(args[2])
	if err != nil {
		return shim.Error("Invalid transaction amount, expecting a integer value")
	}
	// 转账
	Aval = Aval - X
	Bval = Bval + X
	fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)

	// 更新转账后账本中 A 余额
	err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	// 更新转账后账本中 B 余额
	err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
	if err != nil {
		return shim.Error(err.Error())
	}

	return shim.Success(nil)
}

// 账户注销
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}

	A := args[0]   // 账户用户名

	// 从账本中删除该账户状态
	err := stub.DelState(A)
	if err != nil {
		return shim.Error("Failed to delete state")
	}

	return shim.Success(nil)
}

// 账户查询
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	var A string
	var err error

	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
	}

	A = args[0]   // 账户用户名

	// 从账本中获取该账户余额
	Avalbytes, err := stub.GetState(A)
	if err != nil {
		jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
		return shim.Error(jsonResp)
	}

	if Avalbytes == nil {
		jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
		return shim.Error(jsonResp)
	}

	jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
	fmt.Printf("Query Response:%s\n", jsonResp)
	// 返回转账金额
	return shim.Success(Avalbytes)
}

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

4.链码测试

好耶,走到这里,你应该已经学会了链码的基本编写,但是我们编写的链码是需要部署在fabric网络之中的,不能直接运行,在开发过程中可选择单元测试或在fabric中设置dev模式【开发者模式】,将链码部署在网络中,进行测试。

单元测试:无需将链码部署在网络中,操作简单但是不太方便,无法进行富查询【富查询需要在有fabric网络的情况下】

dev模式【建议测试方式】:在fabric网络中的测试,需要将链码部署在fabric网络中。测试较方便,可以进行富查询。但是相较于单元测试,会有些难度。

建议学习方式:

在学习链码的初级阶段,先使用单元测试【对fabric还不是很了解】(如果对fabric已经掌握的不错了,环境搭建方面没什么问题了,可直接使用dev模式进行测试),在之后的学习过程中,使用dev。

单元测试:

参考链接:https://www.cnblogs.com/skzxc/p/12150476.html

fabric中提供了一个MockStub类用于单元测试。

单元测试不需要启动任何网络节点,通过我们的测试文件就可以在本地对链码中的接口进行调用测试。其原理就是在MockStub类中维护一个 map 来模拟 ke,key-val 的状态数据库,链码调用的PutState() 和 GetState() 其实是作用于内存中的map。

MockStub主要提供两个函数来模拟背书节点对链码的调用:MockInit()MockInvoke(),分别调用Init和Invoke接口。接收的参数均为类型为string的uuid(随便设置即可),以及一个二维byte数组(用于测试的提供参数)。二维byte数组的第一个参数为方法名。例如参数为[][]byte{[]byte(“query”),[]byte(“A”)},使用stub.GetFunctionAndParameters方法返回的第一个参数是string类型的方法名,第二个参数为字符串切片类型,在此例中,则方法名为query,参数为[]string{“A”}

单元测试要求:

1.测试文件要以xx_test.go方式命名(即要以文件命名要以_test结尾)

2.测试方法名要以Testxxxx的方式命名(即方法名要以Test开头)

3.要导入testing包

单元测试示例:
package main

import (
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"testing"
)
//测试init方法
func TestSmartContract_Init(t *testing.T) {
	//调用NewMockStub方法返回一个MockStub类,来模拟背书节点对链码的调用
	//接收参数为一个string类型的name,随便写无需注意,和一个chaincode接口类型(即你要测试的链码)
	stub:=shim.NewMockStub("1",new(SimpleChaincode))
	//调用MockInit()方法测试初始化,返回一个response,
	//response.Payload中包含链码执行成功响应的数据,response.Message包含链码执行失败返回的错误信息
	//接收的参数均为类型为string的uuid(随便设置即可),以及一个二维byte数组(用于测试的提供参数)
	re:=stub.MockInit("1",[][]byte{[]byte(""),[]byte("A"),[]byte("100"),[]byte("B"),[]byte("100")})
	if len(re.Message)!=0 {
		fmt.Println(re.Message)
	}
}
//测试query方法
func TestSimpleChaincode_Invoke(t *testing.T) {
	//每一个测试方法都是一个单独的模块,不能在上面的方法中测试了初始化,而对于这次的测试query方法就不        进行初始化,两个方法是没有关联的
	//如果直接调用MockInvoke()取执行query操作,是无法查询到数据的
	//需要先执行初始化方法(即对数据的添加),然后在执行query操作(仅针对于本例操作讲解,在往后链码的        编写中,若无需进行初始化操作,无需执行初始化
	//但是要测试一个查询或者删除,要在用一个方法内,先执行新增的操作,然后再去进行查询或删除操作)
	stub:=shim.NewMockStub("1",new(SimpleChaincode))
	re:=stub.MockInit("1",[][]byte{[]byte(""),[]byte("A"),[]byte("100"),[]byte("B"),[]byte("100")})
	if len(re.Message)!=0 {
		fmt.Println(re.Message)
	}
	re2:=stub.MockInvoke("2",[][]byte{[]byte("query"),[]byte("A")})
	if len(re2.Message)!=0 {
		fmt.Println(re.Message)
	}
	fmt.Println(re2.Payload)
}

测试结果:
xiaoliu
dev测试

【要在搭建好fabric网络环境的基础下】

该链码位于fabric-sample/chaincode/chaincode_example02中,可启动dev网络进行测试

1.启动网络

$ cd ./fabric-samples/chaincode-docker-devmode/
#启动网络
$ docker-compose -f docker-compose-simple.yaml up -d

2.进入链码容器,对链码进行编译

$ docker exec -it chaincode bash
# cd chaincode_example02/go/
# go build
# CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=test:0 ./go

3.打开一个新的终端,进入 cli 容器,安装并示例化链码

$ docker exec -it cli bash
# peer chaincode install -p chaincodedev/chaincode/chaincode_example02/go -n test -v 0
# peer chaincode instantiate -n test -v 0 -c '{"Args":["init","a", "100", "b","200"]}' -C myc

4.查询账户 a 的余额,返回结果为 100

# peer chaincode query -n test -c '{"Args":["query","a"]}' -C myc

5.从账户 a 转账 10 给 b

# peer chaincode invoke -n test -c '{"Args":["invoke","a","b","10"]}' -C myc

6.再次查询账户 b 的余额,返回结果为 90

# peer chaincode query -n test -c '{"Args":["query","a"]}' -C myc

可以在 chaincode 容器中查看到运行的日志:

ex02 Init
Aval = 100, Bval = 200
ex02 Invoke
Query Response:{"Name":"a","Amount":"100"}
ex02 Invoke
Aval = 90, Bval = 210
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}

7.关闭网络

$ docker-compose -f docker-compose-simple.yaml down

**5.从账户 a 转账 10 给 b**

```shell
# peer chaincode invoke -n test -c '{"Args":["invoke","a","b","10"]}' -C myc

6.再次查询账户 b 的余额,返回结果为 90

# peer chaincode query -n test -c '{"Args":["query","a"]}' -C myc

可以在 chaincode 容器中查看到运行的日志:

ex02 Init
Aval = 100, Bval = 200
ex02 Invoke
Query Response:{"Name":"a","Amount":"100"}
ex02 Invoke
Aval = 90, Bval = 210
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}

7.关闭网络

$ docker-compose -f docker-compose-simple.yaml down

关于dev更多详情及部署参考上方链接或文件中的链码开发dev模式.docx

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

fabric链码的编写-入门 的相关文章

  • FISCO BCOS 七、MetaMask测试Ganache搭建的私有网络

    目录 背景介绍 所需软件 1 Ganache 2 MetaMask 操作 第一步 启动Ganache 第二步 使用MetaMask连接Ganache 第三步 开始测试 背景介绍 本文主要介绍如何使用Ganache 在本地搭建以太坊私有网络
  • 区块链之java调用智能合约(二)部署智能合约

    前言 上一节 已经说过 如何的创建一个合约 如何编译合约 然后在java中调用 但是呢 这些还远远不够 那么还差哪些呢 现在就是如何将创建的智能合约部署的对应的共链 私链 测试链中了 需要部署后 才能真正的使用 现在就讲讲如何部署智能合约
  • Truffle测试框架

    Truffle测试框架 Truffle 有一个标准的自动化测试框架 让你可以非常方便地测试您的合约 这个框架允许您以两种不同的方式编写简单可控的测试 1 在 JavaScript中 用于执行来自外部世界的合约 就像您的应用程序一样 2 在
  • ERC20智能合约-带销毁功能

    SPDX License Identifier MIT pragma solidity 0 8 0 dev Interface of the ERC20 standard as defined in the EIP interface IE
  • Fabric中的“账户”体系

    本文的联盟链是以Fabric为例 本文中transaction翻译成事务 而非交易 联盟链相对于公链 最大的不同在于 联盟链上的数据不是对所有人都公开的 只有联盟内的成员才可以访问 写入链上数据 第二个重要的区别 联盟链不发币 所以联盟链里
  • HyperLedger Fabric 实践错误收集

    HyperLedger Fabric 实践错误收集 在ubuntu中通过docker compose启动容器的时候报错 ERROR for cli Cannot create container for service cli Confli
  • 区块链应用开发(智能合约的开发和WeBASE合约IDE的使用)

    文章目录 四 智能合约的开发和WeBASE合约IDE的使用 一 实验概述 二 实验目标 三 实验环境及建议 四 实验步骤 4 1 启动Webase 4 2 智能合约开发 4 2 1 合约功能设计 4 2 2 存证合约开发 4 2 3 工厂合
  • Fabric private data入门实战

    Hyperledger Fabric private data是1 2版本引入的新特性 fabric private data是利用旁支数据库 SideDB 来保存若干个通道成员之间的私有数据 从而在通道之上又提供了一层更灵活的数据保护机制
  • Python Fabric 运行命令返回“binascii.Error:填充不正确”

    我正在尝试使用下面的脚本通过 Fabric 连接到亚马逊 EC2 但我遇到了一个问题 我不知道如何解决 import os from fabric api import run env local cd WORK os getenv HOM
  • Python Fabric:跳过需要密码的登录

    我有一个与此类似的问题 如何跳过要求输入密码的 Fabric 连接 没有答案 我正在寻找一种方法让 Fabric 认为任何要求密码而不是 SSH 密钥登录的主机都是不好的 因为这意味着我正在连接的用户在服务器上没有帐户 而且我迭代大量主机列
  • 如何捕获 Fabric 中的身份验证错误并重试?

    我有两个用户名和相应的密码用于管理我的服务器 有没有办法让我的 fab 脚本 模块使用一个 如果第一个失败则使用第二个 而不必维护完整的凭据列表每个主机甚至一组主机 我在文档中看不到围绕 run 或类似的尝试 除外的方法 run和其他命令引
  • Fabric/Python:AttributeError:“NoneType”对象没有属性“partition”

    Fabric中有以下功能用于添加用户帐户 脚本 fab l Python source code Available commands OS TYPE adduser createcmd Create command line for ad
  • python Fabric是否支持动态设置env.hosts?

    我想动态更改 env hosts 因为有时我想先部署到一台机器 检查是否正常 然后部署到多台机器 目前我需要先设置 env hosts 如何在方法中设置 env hosts 而不是在脚本启动时全局设置 是的 你可以设置env hosts动态
  • 织物密码

    每次结构运行时 它都会要求输入 root 密码 是否可以将其与自动建议一起发送 fab staging test 我知道您已经询问过密码 但是配置系统以便您可以在没有密码的情况下进行结构 即 SSH 不是更好吗 为此 在本地计算机上执行以下
  • 在 Python/Bash 中通过 SSH/Sudo 测试文件/目录是否存在 [重复]

    这个问题在这里已经有答案了 我正在远程服务器上安装证书 并想在覆盖它们之前检查它们是否存在 服务器仅允许通过 ssh 公钥进行非 root 访问 我可以sudo s在 shell 中 root 一次 需要 root 权限 因为其他人无法读取
  • 将不同的参数传递给 Fabric 中的不同主机

    我正在编写一个脚本来在多个主机上启动负载生成实验 我可以编写一个 bash 脚本来启动多个 ssh 会话 但我希望使用更结构化的东西 由于我的大部分脚本都使用 Python 所以我想Fabric http www fabfile org看起
  • 在 Fabric 中作为 sudo 执行

    我有一个命令service app start demo需要我输入sudo service app start demo在命令行中 I used sudo service app start demo and sudo sudo servi
  • Python导入错误:没有名为Fabric.api的模块?

    我收到以下错误 Traceback most recent call last File drayd py line 2 in
  • 在 Windows 7 上安装 Python Fabric 时出现问题

    我正在尝试使用以下指南在 Windows 7 上安装 Python Fabric在 Windows 上安装 Python 和 Fabric http www jonnyreeves co uk 2011 08 getting python
  • Python Fabric - 未找到主机。请指定用于连接的(单个)主机字符串:

    如何获取 找不到主机 请指定用于连接的 单个 主机字符串 面料如何解决 def bootstrap host ec2 54 xxx xxx xxx compute 1 amazonaws com env hosts host env use

随机推荐

  • python统计excel某一列不同类别数量

    功能 某一列 比如病例类型 数据含有多种类别 比如 死亡 存活 失访 三种类别 取值 用python实现统计出这三种类别的数量 输入 xlsx文件 含有病例类型这一列row 4 数据含有 死亡 存活 失访 三种类别取值 输出 三种类别的数量
  • 二叉树链式存储结构代码实现

    上一篇博客已经介绍过实际内容了 这一篇直接上干货代码了 结构体 前面写的是用c语言写的 用的也是递归的方法 typedef char BTDataType typedef struct BTNode struct BTNode left s
  • 给数组元素指定位置排序方法

    调整数组选项 梨子 香蕉 苹果 selectSort gt let finalData const arr name 苹果 code apple name 梨子 code pear name 香蕉 code banana 原数据 const
  • office全家桶_大学新生必备!Adobe+Office全家桶免费拿。

    做最有趣有料的科技自媒体 数一数手指 还有几天就要开学了 各 位准大学生们听我 说 作为曾经的 学长 我很有必要告诉你们一些来自大学的 潜规则 你迟早有一天会用到电脑里装的Adode和offic的软件 Adobe和 office 的软件可以
  • Android 自定义属性,自定义控件、自定义View以及View的常见Error

    Android 自定义属性 自定义控件 自定义View以及View的常见Error View GONE 不占据layout 但是对象还是存在 资源还是占用的 View INVISIBLE 占据layout 对象还是存在 资源还是占用的 只是
  • Java开发代码规范之异常日志(二)——日志规约

    文章通过学习 阿里巴巴Java开发手册 整理 1 应用中不可直接使用日志系统 Log4j Logback 中的API 而应依赖使用日志框架SLF4J中的API 使用门面模式的日志框架 有利于维护和各个类的日志处理方式统一 import or
  • 【华为OD机试】不开心的小朋友(C++ Python Java)2023 B卷

    时间限制 C C 1秒 其他语言 2秒 空间限制 C C 262144K 其他语言524288K 64bit IO Format lld 题目描述 游乐场里增加了一批摇摇车 非常受小朋友欢迎 但是每辆摇摇车同时只能有一个小朋友使用 如果没有
  • 信号与系统——初识到理解(第五章——傅立叶变换的应用)

    目录 第五章 傅立叶变换的应用 信号与系统的时频域特性 5 1 傅立叶变换的模和相位表示 5 2 无失真传输系统 讨论连续情况 离散时足以整数约束即可 5 3 系统相位 5 4 群时延 采样 5 5 通信中对信号的加工 5 6 冲击串采样
  • SM2可以使用私钥加密吗

    是的 SM2可以使用私钥加密 SM2是一种非对称加密算法 它使用公钥和私钥对数据进行加密和解密 私钥是保存在本地的密钥 只有拥有私钥的人才能使用它来加密数据或对已加密的数据进行解密 SM2的私钥加密功能可以用来保护数据的安全性 防止数据在传
  • 文件上传漏洞

    文件上传 文件上传是现代互联网常见的功能 允许用户上传图片 视频 及其他类型文件 向用户提供的功能越多 Web受攻击的风险就越大 文件上传漏洞 上传文件时 如果未对上传的文件进行严格的验证和过滤 就容易造成文件上传漏洞 上传脚本文件 包括a
  • 大数据学习01 -Linux 的简单使用

    1 NAT 网络地址转换 默认使用VMnet8 1 原理 子网掩码 和IP进行与操作 可以得到对应的子网IP 在那个网段上 IP 哪台机制 网关 路由器的IP dns 去dbs服务器查找域名对应的IP 常用命令 修改主机名 vi etc s
  • 13.罗马数字转整数

    这题转成字符数组后遍历就是了 罗马数字包含以下七种字符 I V X L C D 和 M 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如 罗马数字 2 写做 II 即为两个并列的 1 12 写做 X
  • 2023华为OD机试 报数游戏(Python)

    题目 输入 输入一个整数 n 代表有多少人 n 小于 1000 输出 告诉我最后剩下的那个人原来是第几个 示例 输入 2 输出 2 解释 两人围成一圈 第一个人数到3就走了 所以最后剩的是第二个人 理解 想象这样一个游戏 大家围成一圈 从第
  • C++:sort函数

    sort函数用于C 中 对给定区间所有元素进行排序 默认为升序 也可进行降序排序 sort函数进行排序的时间复杂度为n log2n 比冒泡之类的排序算法效率要高 使用sort函数要包含头文件为 include
  • Nginx实现四层代理与七层代理

    目录 一 实验环境准备 1 准备三台服务器 2 安装nginx环境 3 启动nginx环境 二 设置Nginx七层代理 1 proxy代理服务器配置 轮询 2 设置加权轮询 3 健康检测功能 4 解决重复登录问题 源地址哈希ip hash
  • 透视投影

    11
  • Idea使用Maven编译scala和打包jar

    下面Maven的pom文件
  • DBhelper的使用

    建立一个DbHelper cs类文件 复制内容 using System using System Data using System Data SqlClient using System Configuration using Syst
  • FreeImage例子

    http www pudn com downloads525 sourcecode windows detail2176862 html include OpenJPEG h include unistd h OpenJPEG OpenJP
  • fabric链码的编写-入门

    链码的编写 前言 fabric链码的编写较简单 在熟悉了基本结构和相关API之后就可上手编写 但是要多多练习 提高编写链码的速度和正确度 学习步骤 1 熟悉链码的基本结构 2 熟练链码相关API 3 练习 练习 练习 参考链接 https