注意

本文档适用于 Ceph 开发版本。

Lua 脚本

Pacific版本中的新功能。

此功能允许用户为Lua脚本分配执行上下文。支持的上下文有:

  • prerequest将在每次操作执行前执行脚本

  • postrequest将在每次操作执行后执行脚本

  • background将在指定的时间间隔内执行脚本

  • getdata当对象被下载时,将在对象的数据上执行脚本

  • putdata当对象被上传时,将在对象的数据上执行脚本

请求(预或后)或数据(获取或上传)上下文脚本可以限制为属于特定租户的用户的所有操作。全局RGW表全局RGW表rgw_lua_max_memory_per_state配置参数。请注意,Lua及其标准库的基本开销约为32K字节。要禁用此限制,请使用零。rgw_lua_max_runtime_per_state配置参数更改。如果Lua脚本超过此运行时间,它将被终止。要禁用运行时限制,请使用零。

警告

修改内存限制时要小心。如果当前内存使用量超过新设置的限制,背景状态中之前存储的所有数据都将丢失。

警告

禁用运行时限制可能导致无限制的脚本执行,这可能导致过度资源消耗,并可能影响RADOS网关的可用性。

默认情况下,所有Lua标准库在脚本中都是可用的,但是,为了允许在脚本中使用额外的Lua模块,我们支持将软件包添加到允许列表:

  • 确保在现有集群的每个主机上都有luarocks如果主机上安装了

  • 将Lua软件包添加到允许列表,或从其中删除软件包不会安装或删除它。要使更改生效,应调用“重新加载”命令。

  • 此外,所有允许列表中的软件包都使用luarocks软件包管理器在radosgw重新启动时重新安装。

  • 要添加包含需要编译的C源代码的软件包,请使用--allow-compilation标志。在这种情况下,主机上需要提供C编译器

  • Lua软件包安装在radosgw的本地目录中,并从中使用。这意味着允许列表中的Lua软件包与主机上可用的任何Lua软件包是分开的。/tmp/luarocks/<entity name>。其前缀部分(/tmp/luarocks/)可以通过rgw_luarocks_location配置参数设置为不同的位置。$HOME/.luarocks, /usr/lib64/lua, /usr/share/lua).

通过CLI管理脚本

要上传脚本:

# radosgw-admin script put --infile={lua-file-path} --context={prerequest|postrequest|background|getdata|putdata} [--tenant={tenant-name}]
  • 当使用background上下文上传脚本时,不应指定租户名称。

# cephadm shell radosgw-admin script put --infile=/rootfs/{lua-file-path} --context={prerequest|postrequest|background|getdata|putdata} [--tenant={tenant-name}]

要将脚本内容打印到标准输出:

# radosgw-admin script get --context={preRequest|postRequest|background|getdata|putdata} [--tenant={tenant-name}]

要删除脚本:

# radosgw-admin script rm --context={preRequest|postRequest|background|getdata|putdata} [--tenant={tenant-name}]

通过CLI管理软件包

要将软件包添加到允许列表:

# radosgw-admin script-package add --package={package name} [--allow-compilation]

要将特定版本的软件包添加到允许列表:

# radosgw-admin script-package add --package='{package name} {package version}' [--allow-compilation]
  • 当向列表中添加已存在的软件包的不同版本时,新添加的版本将覆盖现有的版本。

  • 当添加未指定版本的软件包时,将添加软件包的最新版本。

要从允许列表中删除软件包:

# radosgw-admin script-package rm --package={package name}

要从允许列表中删除特定版本的软件包:

# radosgw-admin script-package rm --package='{package name} {package version}'
  • 当删除未指定版本的软件包时,将删除软件包的任何现有版本。

要打印允许列表中的软件包列表:

# radosgw-admin script-package list

要将允许列表中的更改应用到所有RGWs:

# radosgw-admin script-package reload

无上下文函数

调试日志

The RGWDebugLog()function接受一个字符串并将其以优先级20打印到调试日志。Lua INFO:前缀。此函数没有返回值。

请求字段

警告

此功能是实验性的。字段可能在将来被删除或重命名。

Note

  • 虽然Lua是一种区分大小写的语言,但radosgw提供的字段名是不区分大小写的。函数名仍然区分大小写。

  • 标记为“可选”的字段可以有nil值。

  • 标记为“可迭代”的字段可以使用pairs()函数和#长度运算符。

  • 所有表字段都可以使用括号运算符[].

  • time字段是具有以下格式的字符串:%Y-%m-%d %H:%M:%S.

字段

类型

描述

可迭代

可写

可选

Request.RGWOp

string

radosgw操作

no

no

no

Request.DecodedURI

string

解码的URI

no

no

no

Request.ContentLength

整数

请求的大小

no

no

no

Request.GenericAttributes

字符串到字符串通用属性映射

no

no

Request.Response

对请求的响应

no

no

no

Request.Response.HTTPStatusCode

整数

HTTP状态码

no

no

Request.Response.HTTPStatus

string

HTTP状态文本

no

no

Request.Response.RGWCode

整数

radosgw错误码

no

no

Request.Response.Message

string

响应消息

no

no

Request.SwiftAccountName

string

swift账户名称

no

no

Request.Bucket

桶的信息

no

no

no

Request.Bucket.Tenant

string

桶的租户

no

no

Request.Bucket.Name

string

桶名称(仅在prerequest上下文中可写)

no

no

Request.Bucket.Marker

string

桶标记(初始ID)

no

no

Request.Bucket.Id

string

桶ID

no

no

Request.Bucket.ZoneGroupId

string

桶的区域组

no

no

Request.Bucket.CreationTime

时间

桶的创建时间

no

no

Request.Bucket.MTime

时间

桶的修改时间

no

no

Request.Bucket.Quota

桶配额

no

no

Request.Bucket.Quota.MaxSize

整数

桶配额最大大小

no

no

no

Request.Bucket.Quota.MaxObjects

整数

桶配额最大对象数

no

no

no

Reques.Bucket.Quota.Enabled

布尔值

桶配额已启用

no

no

no

Request.Bucket.Quota.Rounded

布尔值

桶配额四舍五入到4K

no

no

no

Request.Bucket.PlacementRule

桶放置规则

no

no

Request.Bucket.PlacementRule.Name

string

桶放置规则名称

no

no

no

Request.Bucket.PlacementRule.StorageClass

string

桶放置规则存储类

no

no

no

Request.Bucket.User

string

所有者用户/账户ID

no

no

Request.Object

对象的信息

no

no

Request.Object.Name

string

对象名称

no

no

no

Request.Object.Instance

string

对象版本

no

no

no

Request.Object.Id

string

对象ID

no

no

no

Request.Object.Size

整数

对象大小

no

no

no

Request.Object.MTime

时间

对象mtime

no

no

no

Request.CopyFrom

复制操作的信息

no

no

Request.CopyFrom.Tenant

string

从哪个对象复制租户

no

no

no

Request.CopyFrom.Bucket

string

从哪个桶复制对象

no

no

no

Request.CopyFrom.Object

从哪个对象复制。参见:Request.Object

no

no

Request.ObjectOwner

对象所有者

no

no

no

Request.ObjectOwner.DisplayName

string

对象所有者显示名称

no

no

no

Request.ObjectOwner.User

string

所有者用户/账户ID。参见:Request.Bucket.User

no

no

Request.ZoneGroup.Name

string

区域组的名称

no

no

no

Request.ZoneGroup.Endpoint

string

区域组的端点

no

no

no

Request.UserAcl

用户ACL

no

no

no

Request.UserAcl.Owner

用户ACL所有者。参见:Request.ObjectOwner

no

no

no

Request.UserAcl.Grants

用户ACL从字符串到授权的映射

no

no

Request.UserAcl.Grants["<name>"]

用户ACL授权

no

no

no

Request.UserAcl.Grants["<name>"].Type

整数

用户ACL授权类型

no

no

no

Request.UserAcl.Grants["<name>"].User

string

用户ACL授权用户/账户ID

no

no

no

Request.UserAcl.Grants["<name>"].GroupType

整数

用户ACL授权组类型

no

no

Request.UserAcl.Grants["<name>"].Referer

string

用户ACL授权引用者

no

no

Request.BucketAcl

桶ACL。参见:Request.UserAcl

no

no

no

Request.ObjectAcl

对象ACL。参见:Request.UserAcl

no

no

no

Request.Environment

字符串到字符串环境映射

no

no

Request.Policy

策略

no

no

Request.Policy.Text

string

策略文本

no

no

no

Request.Policy.Id

string

策略ID

no

no

Request.Policy.Statements

字符串语句列表

no

no

Request.UserPolicies

用户策略列表

no

no

Request.UserPolicies[<index>]

用户策略。参见:Request.Policy

no

no

no

Request.RGWId

string

radosgw主机ID:<host>-<zone>-<zonegroup>

no

no

no

Request.HTTP

HTTP头

no

no

no

Request.HTTP.Parameters

字符串到字符串参数映射

no

no

Request.HTTP.Resources

字符串到字符串资源映射

no

no

Request.HTTP.Metadata

字符串到字符串元数据映射

no

Request.HTTP.StorageClass

string

存储类

no

Request.HTTP.Host

string

主机名

no

no

no

Request.HTTP.Method

string

HTTP方法

no

no

no

Request.HTTP.URI

string

URI

no

no

no

Request.HTTP.QueryString

string

HTTP查询字符串

no

no

no

Request.HTTP.Domain

string

域名

no

no

no

Request.Time

时间

请求时间

no

no

no

Request.Dialect

string

“S3”或“Swift”

no

no

no

Request.Id

string

请求ID

no

no

no

Request.TransactionId

string

事务ID

no

no

no

Request.Tags

对象标签映射

no

no

Request.User

触发请求的用户

no

no

no

Request.User.Tenant

string

触发用户的租户

no

no

no

Request.User.Id

string

触发用户ID

no

no

no

Request.Trace

跟踪信息

no

no

no

Request.Trace.Enable

布尔值

跟踪已启用

no

no

请求函数

操作日志

The Request.Log()function将请求打印到操作日志中。此函数没有参数。如果成功,它返回0,如果失败,则返回错误代码。

跟踪

跟踪函数只能在postrequest上下文中使用。

  • Request.Trace.SetAttribute(<key>, <value>)- 设置请求跟踪的属性。key,应该是字符串,第二个是value,可以是字符串或数字(整数或双精度数)。

  • Request.Trace.AddEvent(<name>, <attributes>)- 向请求跟踪的第一个跨度添加事件name的字符串应该是第一个参数,然后是可选的事件attributes,对于没有属性的事件。

背景上下文

The background上下文可用于分析、监控、为其他上下文执行缓存数据等目的。

数据上下文

getdataputdata上下文具有以下字段:Data读取-only且可迭代(字节逐字节)。如果对象在多个块中上传或检索,则Data字段将一次包含一个块的数据。Offset包含块在整个对象中的偏移量。Request字段和背景RGW表也可用。

全局RGW表

The RGWLua表可以从所有上下文中访问,并在执行期间保存写入它的数据,以便稍后在同一上下文或不同上下文中读取和使用。RGWLua表,当守护进程重新启动时它会丢失。请注意,background上下文脚本将在每个实例上运行。RGWLua表使用字符串索引,可以存储以下类型的值:字符串、整数、双精度数和布尔值

增减函数

由于RGW表中的条目可能同时从多个位置访问,我们需要一种原子方式来增减其中的数值。为此,应使用以下函数:RGW.increment(<key>, [value])key的值减少value(如果提供值)或1(如果不提供)RGW.decrement(<key>, [value])key的值减少value(如果提供值)或1(如果不提供)key的值不是数字,脚本执行将失败

Lua代码示例

  • 在复制的情况下打印源和目标对象的信息:

function print_object(object)
  RGWDebugLog("  Name: " .. object.Name)
  RGWDebugLog("  Instance: " .. object.Instance)
  RGWDebugLog("  Id: " .. object.Id)
  RGWDebugLog("  Size: " .. object.Size)
  RGWDebugLog("  MTime: " .. object.MTime)
end

if Request.CopyFrom and Request.Object and Request.CopyFrom.Object then
  RGWDebugLog("copy from object:")
  print_object(Request.CopyFrom.Object)
  RGWDebugLog("to object:")
  print_object(Request.Object)
end
  • 通过“通用函数”打印ACL:

function print_owner(owner)
  RGWDebugLog("Owner:")
  RGWDebugLog("  Display Name: " .. owner.DisplayName)
  RGWDebugLog("  Id: " .. owner.User.Id)
  RGWDebugLog("  Tenant: " .. owner.User.Tenant)
end

function print_acl(acl_type)
  index = acl_type .. "ACL"
  acl = Request[index]
  if acl then
    RGWDebugLog(acl_type .. "ACL Owner")
    print_owner(acl.Owner)
    RGWDebugLog("  there are " .. #acl.Grants .. " grant for owner")
    for k,v in pairs(acl.Grants) do
      RGWDebugLog("    Grant Key: " .. k)
      RGWDebugLog("    Grant Type: " .. v.Type)
      RGWDebugLog("    Grant Group Type: " .. v.GroupType)
      RGWDebugLog("    Grant Referer: " .. v.Referer)
      RGWDebugLog("    Grant User Tenant: " .. v.User.Tenant)
      RGWDebugLog("    Grant User Id: " .. v.User.Id)
    end
  else
    RGWDebugLog("no " .. acl_type .. " ACL in request: " .. Request.Id)
  end
end

print_acl("User")
print_acl("Bucket")
print_acl("Object")
  • 仅在错误情况下使用操作日志:

if Request.Response.HTTPStatusCode ~= 200 then
  RGWDebugLog("request is bad, use ops log")
  rc = Request.Log()
  RGWDebugLog("ops log return code: " .. rc)
end
  • 将值设置到错误消息中:

if Request.Response.HTTPStatusCode == 500 then
  Request.Response.Message = "<Message> something bad happened :-( </Message>"
end
  • 向对象添加客户端未原始发送的元数据:

prerequest

if Request.RGWOp == 'put_obj' then
  Request.HTTP.Metadata["x-amz-meta-mydata"] = "my value"
end

postrequest在我们查看元数据时,上下文为:

RGWDebugLog("number of metadata entries is: " .. #Request.HTTP.Metadata)
for k, v in pairs(Request.HTTP.Metadata) do
  RGWDebugLog("key=" .. k .. ", " .. "value=" .. v)
end
  • 使用模块创建基于Unix套接字的JSON编码“访问日志”:

首先,我们应该将以下软件包添加到允许列表:

# radosgw-admin script-package add --package=lua-cjson --allow-compilation
# radosgw-admin script-package add --package=luasocket --allow-compilation

然后,运行一个服务器来监听Unix套接字。例如,使用“netcat”:

# rm -f /tmp/socket
# nc -vklU /tmp/socket

最后,重新启动radosgw并上传以下脚本到postrequest上下文:

if Request.RGWOp == "get_obj" then
  local json = require("cjson")
  local socket = require("socket")
  local unix = require("socket.unix")
  local s = assert(unix())
  E = {}

  msg = {bucket = (Request.Bucket or (Request.CopyFrom or E).Bucket).Name,
    time = Request.Time,
    operation = Request.RGWOp,
    http_status = Request.Response.HTTPStatusCode,
    error_code = Request.Response.HTTPStatus,
    object_size = Request.Object.Size,
    trans_id = Request.TransactionId}

  assert(s:connect("/tmp/socket"))
  assert(s:send(json.encode(msg).."\n"))
  assert(s:close())
end
  • 仅跟踪特定桶的请求

跟踪默认情况下是禁用的,因此我们应该为此特定桶启用跟踪

if Request.Bucket.Name == "my-bucket" then
    Request.Trace.Enable = true
end

如果跟踪已启用在RGW上,Request.Trace.Enable的值为true,因此我们应该禁用与桶名称不匹配的所有其他请求的跟踪。prerequest上下文:

if Request.Bucket.Name ~= "my-bucket" then
    Request.Trace.Enable = false
end

注意,更改Request.Trace.Enable不会改变跟踪器的状态,但会禁用或启用仅针对请求的跟踪。

  • 为请求跟踪添加信息

in postrequest

Request.Trace.AddEvent("lua script execution started")

Request.Trace.SetAttribute("HTTPStatusCode", Request.Response.HTTPStatusCode)

event_attrs = {}
for k,v in pairs(Request.GenericAttributes) do
  event_attrs[k] = v
end

Request.Trace.AddEvent("second event", event_attrs)
  • 对象的熵值可用于检测对象是否加密。

。Cephadm 还支持使用putdata上下文中,添加以下脚本

function object_entropy()
        local byte_hist = {}
        local byte_hist_size = 256
        for i = 1,byte_hist_size do
                byte_hist[i] = 0
        end
        local total = 0

        for i, c in pairs(Data)  do
                local byte = c:byte() + 1
                byte_hist[byte] = byte_hist[byte] + 1
                total = total + 1
        end

        entropy = 0

        for _, count in ipairs(byte_hist) do
                if count ~= 0 then
                        local p = 1.0 * count / total
                        entropy = entropy - (p * math.log(p)/math.log(byte_hist_size))
                end
        end

        return entropy
end

local full_name = Request.Bucket.Name.."\\"..Request.Object.Name
RGWDebugLog("entropy of chunk of: " .. full_name .. " at offset:" .. tostring(Offset)  ..  " is: " .. tostring(object_entropy()))
RGWDebugLog("payload size of chunk of: " .. full_name .. " is: " .. #Data)

由 Ceph 基金会带给您

Ceph 文档是一个社区资源,由非盈利的 Ceph 基金会资助和托管Ceph Foundation. 如果您想支持这一点和我们的其他工作,请考虑加入现在加入.