AWX Project Schedules API 详解:rrule 格式规范与项目定时任务实战
【免费下载链接】awxAWX provides a web-based user interface, REST API, and task engine built on top of Ansible. It is one of the upstream projects for Red Hat Ansible Automation Platform.项目地址: https://gitcode.com/gh_mirrors/aw/awx
导读
Project Schedules(项目调度)是 AWX 中用于让 Project(项目)按指定时间规律自动触发 SCM 更新等操作的核心能力。本文以 AWX 仓库中的 API 文档模板awx/api/templates/api/project_schedules_list.md为主体骨架,系统讲解该端点支持的 GET 列表、POST 创建两种操作,并深入剖析其底层rrule(iCalendar 循环规则)的格式约束、校验逻辑与示例。读完本文,你将掌握如何通过 REST API 为 Project 创建定时更新调度,并能准确写出通过校验的rrule字符串。
端点概览:Project Schedules 能做什么
在 AWX 中,ProjectSchedulesList视图对应路由project_schedules_list,注册于 awx/api/urls/project.py#L35:
re_path(r'^(?P<pk>[0-9]+)/schedules/$', ProjectSchedulesList.as_view(), name='project_schedules_list'),也就是说,对单个 Project 的调度端点形如GET/POST /api/v2/projects/{id}/schedules/。该视图定义于 awx/api/views/init.py#L1016:
class ProjectSchedulesList(SubListCreateAPIView): name = _("Project Schedules") model = models.Schedule serializer_class = serializers.ScheduleSerializer parent_model = models.Project relationship = 'schedules' parent_key = 'unified_job_template' resource_purpose = 'schedules of a project'关键点在于parent_key = 'unified_job_template':Project 通过UnifiedJobTemplate统一作业模板基类与Schedule模型关联,而Schedule模型上的外键unified_job_template(见 awx/main/models/schedules.py#L131)正是这种关系的落点。因此,Project Schedules 本质上是"挂在统一作业模板上的定时触发规则",其触发的动作是 Project Update(项目更新)。
从模板继承关系看,project_schedules_list.md继承自 sub_list_create_api_view.md,后者又引入 sub_list_api_view.md,从而形成"列表 + 创建"两个操作的文档结构。
GET:列出某 Project 下的所有调度
对GET /api/v2/projects/{id}/schedules/发起请求,即可检索与所选 Project 关联的全部调度。该列表是标准的分页列表,返回的是 ScheduleSerializer 序列化后的字段集合。
Schedule模型(awx/main/models/schedules.py#L123)的核心字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
unified_job_template | ForeignKey | 关联的作业模板(此处为 Project),同一模板下调度名唯一(unique_together = ('unified_job_template', 'name')) |
name | CharField(max_length=512) | 调度名称 |
enabled | BooleanField(default=True) | 是否启用该调度,关闭后不再处理 |
rrule | TextField | iCalendar 格式的循环规则字符串,即本文重点 |
dtstart | DateTimeField | 计算字段,表示首次执行时间(大于等于该时间的第一次出现) |
dtend | DateTimeField | 计算字段,表示最后一次执行时间,之后调度过期 |
next_run | DateTimeField | 计算字段,下一次执行时间 |
timezone/until | 只读属性 | 由rrule推导出的时区与结束时间 |
其中timezone属性(awx/main/models/schedules.py#L167)会解析rrule中DTSTART的时区信息:若为 UTC 直接返回'UTC',否则通过 zoneinfo 数据库反查时区名。列表默认按next_run降序排列,null 值排最后。
POST:为 Project 创建新调度
对同一端点发起POST请求,可在指定 Project 下创建新的调度,需提交调度相关字段。POST 请求的rrule值必须遵循特定格式,且仅允许规则集的一个子集,详细约束见下文。
关于关联语义需要区分两点:由于本端点parent_key = 'unified_job_template',创建出的 Schedule 直接与 Project(经统一作业模板)绑定;若父模型字段不同,同一文档模板族中的其他子列表端点(如job_template_schedules_list、inventory_source_schedules_list,参见 awx/api/templates/api/ 下的同名模板)会呈现"关联/取消关联已有对象"的语义,而 Project Schedules 走的是直接创建路径。
调度一旦创建并启用,AWX 的调度器(awx/main/scheduler目录)会依据next_run到期触发,Schedule.get_job_kwargs()(awx/main/models/schedules.py#L284)会基于调度的prompts_dict()配置拼接作业参数,并以launch_type: 'scheduled'、schedule: self的方式标记作业来源。
rrule 格式规范:AWX 支持的规则子集
POST 请求中的rrule必须符合以下格式与约束(出自 awx/api/templates/api/_schedule_detail.md):
DTSTART必填,且必须采用DTSTART:YYYYMMDDTHHMMSSZ格式;DTSTART必须为 UTC 时间;INTERVAL必填;- 不支持
SECONDLY(按秒); RRULE关键字必须位于规则语句之前;BYDAY受支持,但不支持带数字前缀的形式(如20MO);BYYEARDAY与BYWEEKNO不支持;- 每条调度只支持一条
RRULE语句; COUNT必须小于 1000。
这些约束在序列化器层有严格对等的实现。ScheduleSerializer.validate_rrule(awx/api/serializers.py#L5687)会逐条拒绝:
- 缺少
DTSTART(提示必须以DTSTART:YYYYMMDDTHHMMSSZ开头); DTSTART为 naive datetime(缺少时区,提示DTSTART cannot be a naive datetime);- 出现多个
DTSTART(提示Multiple DTSTART is not supported); - 未包含
rrule:关键字(提示One or more rule required in rrule); - 包含
exdate:(EXDATE not allowed in rrule); - 包含
rdate:(RDATE not allowed in rrule); - 规则中缺少
INTERVAL,或INTERVAL不是正整数; - 使用了
secondly; BYDAY带数字前缀(正则.*?BYDAY[\:\=][0-9]+[a-zA-Z]{2}命中即拒绝);COUNT > 999(提示COUNT > 999 is unsupported);- 最终通过
Schedule.rrulestr(rrule_value)做整体解析,解析失败则报rrule parsing failed validation。
校验通过后,调度写入数据库。Schedule.rrulestr(awx/main/models/schedules.py#L257)还封装了自定义解析逻辑:先将UNTIL中按 RFC5545 允许的纯日期形式(naive)强制转换为与DTSTART一致的时区感知形式(coerce_naive_until,见 awx/main/models/schedules.py#L197),再交给底层 rrule 库解析为rruleset,为next_run、dtstart、dtend等计算字段提供依据。
rrule 示例全集与解读
以下是文档中给出的 13 条合法rrule示例,覆盖了从"分钟级一次性"到"按年"的常见调度需求,可直接作为 POST 请求体中的rrule值使用:
"DTSTART:20500331T055000Z RRULE:FREQ=MINUTELY;INTERVAL=10;COUNT=5" "DTSTART:20240331T075000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1" "DTSTART:20140331T075000Z RRULE:FREQ=MINUTELY;INTERVAL=1;UNTIL=20230401T075000Z" "DTSTART:20140331T075000Z RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,FR" "DTSTART:20140331T075000Z RRULE:FREQ=WEEKLY;INTERVAL=5;BYDAY=MO" "DTSTART:20140331T075000Z RRULE:FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=6" "DTSTART:20140331T075000Z RRULE:FREQ=MONTHLY;INTERVAL=1;BYSETPOS=4;BYDAY=SU" "DTSTART:20140331T075000Z RRULE:FREQ=MONTHLY;INTERVAL=1;BYSETPOS=-1;BYDAY=MO,TU,WE,TH,FR" "DTSTART:20140331T075000Z RRULE:FREQ=MONTHLY;INTERVAL=1;BYSETPOS=-1;BYDAY=MO,TU,WE,TH,FR,SA,SU" "DTSTART:20140331T075000Z RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=4;BYMONTHDAY=1" "DTSTART:20140331T075000Z RRULE:FREQ=YEARLY;INTERVAL=1;BYSETPOS=-1;BYMONTH=8;BYDAY=SU" "DTSTART:20140331T075000Z RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20230401T075000Z;BYDAY=MO,WE,FR" "DTSTART:20140331T075000Z RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20230610T075000Z"逐条解读:
- 第 1 条:从 2050-03-31 05:50:00(UTC)起,每 10 分钟触发一次,共 5 次(
COUNT限次,适合一次性短周期任务)。 - 第 2 条:每天一次、只触发 1 次,等价于"仅执行一次"的日调度写法。
- 第 3 条:每分钟触发,直到
UNTIL=20230401T075000Z为止(UNTIL定终止时间,与COUNT二选一)。 - 第 4 条:每周一、三、五各触发一次(
BYDAY多值并列)。 - 第 5 条:每 5 周触发一次,仅限周一。
- 第 6 条:每月 6 日触发一次(
BYMONTHDAY指定月内日)。 - 第 7 条:每月第 4 个星期日触发(
BYSETPOS=4+BYDAY=SU组合出"第几个星期几")。 - 第 8 条:每月最后一个工作日(周一至周五)触发(
BYSETPOS=-1表示倒数第一个,BYDAY=MO,TU,WE,TH,FR限定工作日)。 - 第 9 条:每月最后一天触发(
BYSETPOS=-1+ 周一到周日全量)。 - 第 10 条:每年 4 月 1 日触发。
- 第 11 条:每年 8 月的最后一个星期日触发(
BYMONTH=8+BYSETPOS=-1+BYDAY=SU,典型的"某月最后一个星期几"写法)。 - 第 12 条:每周一、三、五触发,且受
UNTIL截止时间约束(UNTIL与BYDAY可同时出现)。 - 第 13 条:每小时触发一次,直到 2023-06-10 07:50:00(UTC)为止。
一个常见的 POST 请求体示例(为某 Project 创建"每天 07:50 UTC 更新一次"的调度)形如:
{ "name": "nightly-project-update", "rrule": "DTSTART:20240331T075000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1", "enabled": true }注意DTSTART是调度的"生效起点"而非首次执行时间本身:首次执行发生在DTSTART之后按规则计算出的第一个时刻,即next_run字段所标示的时间。若希望调度长期有效,可用UNTIL或省略终止条件;若只需要有限次数,用COUNT(须小于 1000)。
校验与调试:用源码确认边界
如果你需要预演某条rrule能产生多少次执行,AWX 还提供了SchedulePreviewSerializer(awx/api/serializers.py#L5671),它接收rrule并复用同一套validate_rrule逻辑,返回按该规则计算出的未来执行时间序列,适合在创建调度前进行预览验证。
提交rrule时若违反上述任一约束,API 会返回 400 及具体的错误消息(如COUNT > 999 is unsupported、Multiple DTSTART is not supported)。因此实践中的要点可以归纳为:
- 永远以
DTSTART:YYYYMMDDTHHMMSSZ(UTC)开头; - 永远在
RRULE:之后声明FREQ与INTERVAL; - 避免
SECONDLY、BYYEARDAY、BYWEEKNO、带数字前缀的BYDAY、EXDATE、RDATE; COUNT控制在 999 以内,且不要与UNTIL同时使用;- 一条调度只写一条
RRULE语句。
文档模板体系:这份规范从哪里来
project_schedules_list.md本身是 AWX 自动生成 API 文档体系中的一个 Jinja2 模板:它继承sub_list_create_api_view.md获得 GET 列表与 POST 创建的结构骨架,再通过{% block post_create %}引入 _schedule_list_common.md,后者强调"POST 请求必须包含符合格式约束的rrule",并继续引入 _schedule_detail.md 展开 rrule 的完整格式细则。相同的调度模板块还被schedule_list.md、job_template_schedules_list.md、inventory_source_schedules_list.md复用(见 awx/api/templates/api/),这意味着本文讲解的 rrule 约束不仅适用于 Project,也适用于 Job Template、Inventory Source 等所有挂载调度的资源,一通则百通。
小结
Project Schedules 端点把"定时更新项目"这一高频运维诉求收敛为一次简单的POST:只要提交合法的rrule,AWX 就会持久化调度、计算next_run并在到期时自动发起带launch_type='scheduled'标记的项目更新作业。理解rrule的格式子集(UTC 的DTSTART、必填的INTERVAL、受限制的BYDAY/BYSETPOS/COUNT)与序列化器层的逐项校验,是写出稳定可用的定时任务的第一步,也是排查 400 报错的关键依据。
【免费下载链接】awxAWX provides a web-based user interface, REST API, and task engine built on top of Ansible. It is one of the upstream projects for Red Hat Ansible Automation Platform.项目地址: https://gitcode.com/gh_mirrors/aw/awx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考