feature: support some new params and fix some issues of zun ui
1. add health check 2. add exposed ports 3. add auto remove 4. add entrypoint 5. add validator to some key-inputs 6. fix the image resource by glance or docker 7. fix step name from miscellaneous to others Change-Id: I40180a44657ace3419c009f73ab527c99fb483d3
This commit is contained in:
parent
e678f9c92b
commit
5f5b8c3cd5
22
releasenotes/notes/FIX-ZUN-UI-997f060449876f33.yaml
Normal file
22
releasenotes/notes/FIX-ZUN-UI-997f060449876f33.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support some new params when create the zun container:
|
||||
|
||||
1. add health check
|
||||
|
||||
2. add exposed ports
|
||||
|
||||
3. add auto remove
|
||||
|
||||
4. add entrypoint
|
||||
|
||||
5. add validator to some key-inputs
|
||||
|
||||
fixes:
|
||||
- |
|
||||
Fix image resource and step name
|
||||
|
||||
1. fix the image resource by glance or docker
|
||||
|
||||
2. fix step name from miscellaneous to others
|
@ -112,7 +112,7 @@ export const apiVersionMaps = {
|
||||
},
|
||||
zun: {
|
||||
key: 'OpenStack-API-Version',
|
||||
value: 'container 1.11',
|
||||
value: 'container 1.40',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -61,6 +61,7 @@
|
||||
"Add Custom Metadata": "Add Custom Metadata",
|
||||
"Add Data Disks": "Add Data Disks",
|
||||
"Add Environment Variable": "Add Environment Variable",
|
||||
"Add Exposed Ports": "Add Exposed Ports",
|
||||
"Add External Members": "Add External Members",
|
||||
"Add Extra Info": "Add Extra Info",
|
||||
"Add Extra Spec": "Add Extra Spec",
|
||||
@ -272,6 +273,7 @@
|
||||
"CIDR": "CIDR",
|
||||
"CIDR Format Error(e.g. 192.168.0.0/24, 2001:DB8::/48)": "CIDR Format Error(e.g. 192.168.0.0/24, 2001:DB8::/48)",
|
||||
"CIFS": "CIFS",
|
||||
"CMD": "CMD",
|
||||
"COE": "COE",
|
||||
"COE Version": "COE Version",
|
||||
"CPU": "CPU",
|
||||
@ -375,6 +377,7 @@
|
||||
"Cold Migrate": "Cold Migrate",
|
||||
"Colombia": "Colombia",
|
||||
"Command": "Command",
|
||||
"Command to run to check health": "Command to run to check health",
|
||||
"Command was successfully executed at container {name}.": "Command was successfully executed at container {name}.",
|
||||
"Commas ‘,’ are not allowed to be in a tag name in order to simplify requests that specify lists of tags": "Commas ‘,’ are not allowed to be in a tag name in order to simplify requests that specify lists of tags",
|
||||
"Commit Latency(ms)": "Commit Latency(ms)",
|
||||
@ -422,6 +425,7 @@
|
||||
"Connection Examples": "Connection Examples",
|
||||
"Connection Information": "Connection Information",
|
||||
"Connection Limit": "Connection Limit",
|
||||
"Consecutive failures needed to report unhealthy": "Consecutive failures needed to report unhealthy",
|
||||
"Console": "Console",
|
||||
"Console Interface": "Console Interface",
|
||||
"Consumer": "Consumer",
|
||||
@ -758,6 +762,7 @@
|
||||
"Do not reset the normally mounted volume to the \"available\"、\"maintenance\" or \"error\" status. The reset state does not remove the volume from the instance. If you need to remove the volume from the instance, please go to the console of the corresponding project and use the \"detach\" operation.": "Do not reset the normally mounted volume to the \"available\"、\"maintenance\" or \"error\" status. The reset state does not remove the volume from the instance. If you need to remove the volume from the instance, please go to the console of the corresponding project and use the \"detach\" operation.",
|
||||
"Do not set with a backend": "Do not set with a backend",
|
||||
"Docker": "Docker",
|
||||
"Docker Hub": "Docker Hub",
|
||||
"Docker Storage Driver": "Docker Storage Driver",
|
||||
"Docker Swarm": "Docker Swarm",
|
||||
"Docker Swarm Mode": "Docker Swarm Mode",
|
||||
@ -783,6 +788,7 @@
|
||||
"Driver Interface": "Driver Interface",
|
||||
"Duplicate tag name: {tag}": "Duplicate tag name: {tag}",
|
||||
"EGP": "EGP",
|
||||
"ENTRYPOINT": "ENTRYPOINT",
|
||||
"ESP": "ESP",
|
||||
"Each instance belongs to at least one security group, which needs to be specified when it is created. Instances in the same security group can communicate with each other on the network, and instances in different security groups are disconnected from the internal network by default.": "Each instance belongs to at least one security group, which needs to be specified when it is created. Instances in the same security group can communicate with each other on the network, and instances in different security groups are disconnected from the internal network by default.",
|
||||
"Each new connection request is assigned to the next server in order, and all requests are finally divided equally among all servers. Commonly used for short connection services, such as HTTP services.": "Each new connection request is assigned to the next server in order, and all requests are finally divided equally among all servers. Commonly used for short connection services, such as HTTP services.",
|
||||
@ -848,6 +854,7 @@
|
||||
"Enable DHCP": "Enable DHCP",
|
||||
"Enable Domain": "Enable Domain",
|
||||
"Enable Floating IP": "Enable Floating IP",
|
||||
"Enable Health Check": "Enable Health Check",
|
||||
"Enable HealthMonitor": "Enable HealthMonitor",
|
||||
"Enable Load Balancer": "Enable Load Balancer",
|
||||
"Enable Neutron Agent": "Enable Neutron Agent",
|
||||
@ -857,6 +864,7 @@
|
||||
"Enable Service": "Enable Service",
|
||||
"Enable User": "Enable User",
|
||||
"Enable auto heal": "Enable auto heal",
|
||||
"Enable auto remove": "Enable auto remove",
|
||||
"Enable compute host": "Enable compute host",
|
||||
"Enable interactive mode": "Enable interactive mode",
|
||||
"Enabled": "Enabled",
|
||||
@ -901,6 +909,7 @@
|
||||
"Expires At": "Expires At",
|
||||
"Export Location": "Export Location",
|
||||
"Export Locations": "Export Locations",
|
||||
"Exposed Ports": "Exposed Ports",
|
||||
"Extend Root Volume": "Extend Root Volume",
|
||||
"Extend Share": "Extend Share",
|
||||
"Extend Volume": "Extend Volume",
|
||||
@ -1016,6 +1025,7 @@
|
||||
"Gibraltar": "Gibraltar",
|
||||
"Given IP": "Given IP",
|
||||
"Glance": "Glance",
|
||||
"Glance Image": "Glance Image",
|
||||
"Global Setting": "Global Setting",
|
||||
"GlusterFS": "GlusterFS",
|
||||
"Grant Databases Access": "Grant Databases Access",
|
||||
@ -1037,6 +1047,10 @@
|
||||
"Hard Reboot": "Hard Reboot",
|
||||
"Hard Rebooting": "Hard Rebooting",
|
||||
"Hash": "Hash",
|
||||
"Health Check CMD": "Health Check CMD",
|
||||
"Health Check Interval": "Health Check Interval",
|
||||
"Health Check Retries": "Health Check Retries",
|
||||
"Health Check Timeout": "Health Check Timeout",
|
||||
"Health Checking Log": "Health Checking Log",
|
||||
"Health Monitor Delay": "Health Monitor Delay",
|
||||
"Health Monitor Detail": "Health Monitor Detail",
|
||||
@ -1124,6 +1138,7 @@
|
||||
"If OS is Linux, system will reset root password, if OS is Windows, system will reset Administrator password.": "If OS is Linux, system will reset root password, if OS is Windows, system will reset Administrator password.",
|
||||
"If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?": "If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?",
|
||||
"If checked, the network will be enable.": "If checked, the network will be enable.",
|
||||
"If exposed port is specified, this parameter will be ignored.": "If exposed port is specified, this parameter will be ignored.",
|
||||
"If it is an SNI type certificate, a domain name needs to be specified": "If it is an SNI type certificate, a domain name needs to be specified",
|
||||
"If it’s not set, the value of this in template will be used.": "If it’s not set, the value of this in template will be used.",
|
||||
"If no gateway is specified, the first IP address will be defaulted.": "If no gateway is specified, the first IP address will be defaulted.",
|
||||
@ -1133,6 +1148,7 @@
|
||||
"If the capacity of the disk is large,the type modify operation may takes several hours. Please be cautious.": "If the capacity of the disk is large,the type modify operation may takes several hours. Please be cautious.",
|
||||
"If the value is set to 0, it means unlimited": "If the value is set to 0, it means unlimited",
|
||||
"If the volume associated with the snapshot has changed the volume type, please modify this option manually; if the volume associated with the snapshot keeps the volume type unchanged, please ignore this option. (no need to change).": "If the volume associated with the snapshot has changed the volume type, please modify this option manually; if the volume associated with the snapshot keeps the volume type unchanged, please ignore this option. (no need to change).",
|
||||
"If this parameter is specified, Zun will create a security group with a set of rules to open the ports that should be exposed, and associate the security group to the container.": "If this parameter is specified, Zun will create a security group with a set of rules to open the ports that should be exposed, and associate the security group to the container.",
|
||||
"If you are not authorized to access any project, or if the project you are involved in has been deleted or disabled, contact the platform administrator to reassign the project": "If you are not authorized to access any project, or if the project you are involved in has been deleted or disabled, contact the platform administrator to reassign the project",
|
||||
"If you are not sure which authentication method to use, please contact your administrator.": "If you are not sure which authentication method to use, please contact your administrator.",
|
||||
"If you choose a port which subnet is different from the subnet of LB, please ensure connectivity between the two.": "If you choose a port which subnet is different from the subnet of LB, please ensure connectivity between the two.",
|
||||
@ -1147,7 +1163,6 @@
|
||||
"Image Info": "Image Info",
|
||||
"Image Name": "Image Name",
|
||||
"Image Pending Upload": "Image Pending Upload",
|
||||
"Image Pull Policy": "Image Pull Policy",
|
||||
"Image Size": "Image Size",
|
||||
"Image Snapshot Pending": "Image Snapshot Pending",
|
||||
"Image Uploading": "Image Uploading",
|
||||
@ -1432,6 +1447,7 @@
|
||||
"Max Retry": "Max Retry",
|
||||
"Max connect": "Max connect",
|
||||
"Maximum interval time for each health check response": "Maximum interval time for each health check response",
|
||||
"Maximum time to allow one check to run in seconds": "Maximum time to allow one check to run in seconds",
|
||||
"Mayotte": "Mayotte",
|
||||
"Mem": "Mem",
|
||||
"Member Count": "Member Count",
|
||||
@ -1727,14 +1743,19 @@
|
||||
"Please input cipher": "Please input cipher",
|
||||
"Please input cluster name": "Please input cluster name",
|
||||
"Please input cluster template name": "Please input cluster template name",
|
||||
"Please input complete data": "Please input complete data",
|
||||
"Please input container name": "Please input container name",
|
||||
"Please input file name": "Please input file name",
|
||||
"Please input image": "Please input image",
|
||||
"Please input ipv4": "Please input ipv4",
|
||||
"Please input ipv6": "Please input ipv6",
|
||||
"Please input key": "Please input key",
|
||||
"Please input key and value": "Please input key and value",
|
||||
"Please input key size": "Please input key size",
|
||||
"Please input metadata": "Please input metadata",
|
||||
"Please input name": "Please input name",
|
||||
"Please input or load Template from a file": "Please input or load Template from a file",
|
||||
"Please input port and protocol": "Please input port and protocol",
|
||||
"Please input prefix": "Please input prefix",
|
||||
"Please input protocol number if it absent in select list.": "Please input protocol number if it absent in select list.",
|
||||
"Please input provider": "Please input provider",
|
||||
@ -1759,6 +1780,7 @@
|
||||
"Please select a subnet!": "Please select a subnet!",
|
||||
"Please select a type!": "Please select a type!",
|
||||
"Please select availability zone": "Please select availability zone",
|
||||
"Please select image driver": "Please select image driver",
|
||||
"Please select item!": "Please select item!",
|
||||
"Please select key": "Please select key",
|
||||
"Please select login type!": "Please select login type!",
|
||||
@ -2301,6 +2323,7 @@
|
||||
"The disk size in GiB for per container": "The disk size in GiB for per container",
|
||||
"The domain name can only be composed of letters, numbers, dashes, in A dash cannot be at the beginning or end, and a single string cannot exceed more than 63 characters, separated by dots; At most can support 30 domain names, separated by commas;The length of a single domain name does not exceed 100 characters, and the total length degree does not exceed 1024 characters.": "The domain name can only be composed of letters, numbers, dashes, in A dash cannot be at the beginning or end, and a single string cannot exceed more than 63 characters, separated by dots; At most can support 30 domain names, separated by commas;The length of a single domain name does not exceed 100 characters, and the total length degree does not exceed 1024 characters.",
|
||||
"The entire inspection process takes 5 to 10 minutes, so you need to be patient. After the registration is completed, the node configuration status will return to the manageable status.": "The entire inspection process takes 5 to 10 minutes, so you need to be patient. After the registration is completed, the node configuration status will return to the manageable status.",
|
||||
"The entrypoint which overwrites the default ENTRYPOINT of the image": "The entrypoint which overwrites the default ENTRYPOINT of the image",
|
||||
"The feasible configuration of cloud-init or cloudbase-init service in the image is not synced to image's properties, so the Login Name is unknown.": "The feasible configuration of cloud-init or cloudbase-init service in the image is not synced to image's properties, so the Login Name is unknown.",
|
||||
"The file with the same name will be overwritten.": "The file with the same name will be overwritten.",
|
||||
"The floating IP configured with port forwardings cannot be bound": "The floating IP configured with port forwardings cannot be bound",
|
||||
@ -2329,6 +2352,7 @@
|
||||
"The name of the physical network to which a port is connected": "The name of the physical network to which a port is connected",
|
||||
"The name should contain letter or number, the length is 1 to 16, characters can only contain \"0-9, a-z, A-Z, -, _.\"": "The name should contain letter or number, the length is 1 to 16, characters can only contain \"0-9, a-z, A-Z, -, _.\"",
|
||||
"The name should contain letter or number, the length is 2 to 64, characters can only contain \"0-9, a-z, A-Z, -, _.\"": "The name should contain letter or number, the length is 2 to 64, characters can only contain \"0-9, a-z, A-Z, -, _.\"",
|
||||
"The name should start with letter or number, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "The name should start with letter or number, characters can only contain \"0-9, a-z, A-Z, -, _, .\"",
|
||||
"The name should start with upper letter or lower letter, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].:^\".": "The name should start with upper letter or lower letter, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].:^\".",
|
||||
"The name should start with upper letter or lower letter, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "The name should start with upper letter or lower letter, characters can only contain \"0-9, a-z, A-Z, -, _, .\"",
|
||||
"The name should start with upper letter, lower letter or chinese, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].\".": "The name should start with upper letter, lower letter or chinese, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].\".",
|
||||
@ -2389,6 +2413,7 @@
|
||||
"This will delete all child objects of the load balancer.": "This will delete all child objects of the load balancer.",
|
||||
"Threads Activity Trends": "Threads Activity Trends",
|
||||
"Time Interval: ": "Time Interval: ",
|
||||
"Time between running the check in seconds": "Time between running the check in seconds",
|
||||
"Timeout(Minute)": "Timeout(Minute)",
|
||||
"Timeout(s)": "Timeout(s)",
|
||||
"To open": "To open",
|
||||
|
@ -61,6 +61,7 @@
|
||||
"Add Custom Metadata": "添加自定义元数据",
|
||||
"Add Data Disks": "添加数据盘",
|
||||
"Add Environment Variable": "添加环境变量",
|
||||
"Add Exposed Ports": "添加服务端口",
|
||||
"Add External Members": "添加外部成员",
|
||||
"Add Extra Info": "添加额外信息",
|
||||
"Add Extra Spec": "添加额外规格",
|
||||
@ -272,6 +273,7 @@
|
||||
"CIDR": "网络地址",
|
||||
"CIDR Format Error(e.g. 192.168.0.0/24, 2001:DB8::/48)": "CIDR格式错误(如:192.168.0.0/24, 2001:DB8::/48)",
|
||||
"CIFS": "CIFS",
|
||||
"CMD": "运行命令(CMD)",
|
||||
"COE": "容器编排引擎",
|
||||
"COE Version": "容器编排引擎版本",
|
||||
"CPU": "CPU",
|
||||
@ -375,6 +377,7 @@
|
||||
"Cold Migrate": "冷迁移",
|
||||
"Colombia": "哥伦比亚",
|
||||
"Command": "命令",
|
||||
"Command to run to check health": "运行以检查运行状况的命令",
|
||||
"Command was successfully executed at container {name}.": "命令已在容器 {name} 上成功执行。",
|
||||
"Commas ‘,’ are not allowed to be in a tag name in order to simplify requests that specify lists of tags": "标记名称中不允许使用英文逗号“,”,以简化指定标记列表的请求",
|
||||
"Commit Latency(ms)": "提交延迟(毫秒)",
|
||||
@ -422,6 +425,7 @@
|
||||
"Connection Examples": "连接示例",
|
||||
"Connection Information": "连接信息",
|
||||
"Connection Limit": "连接限制",
|
||||
"Consecutive failures needed to report unhealthy": "报告不健康需要连续失败次数",
|
||||
"Console": "控制台",
|
||||
"Console Interface": "Console接口",
|
||||
"Consumer": "消费者",
|
||||
@ -758,6 +762,7 @@
|
||||
"Do not reset the normally mounted volume to the \"available\"、\"maintenance\" or \"error\" status. The reset state does not remove the volume from the instance. If you need to remove the volume from the instance, please go to the console of the corresponding project and use the \"detach\" operation.": "请勿将正常的挂载中的云硬盘重置为“可用”、“维护”或”错误“状态。重置状态并不会将云硬盘从云主机上卸载下来。如果您需要将云硬盘从云主机上移除,请进入相应项目的控制台使用“解绑”操作。",
|
||||
"Do not set with a backend": "不设置后端",
|
||||
"Docker": "Docker",
|
||||
"Docker Hub": "Docker Hub",
|
||||
"Docker Storage Driver": "Docker存储驱动",
|
||||
"Docker Swarm": "Docker Swarm",
|
||||
"Docker Swarm Mode": "Docker Swarm Mode",
|
||||
@ -783,6 +788,7 @@
|
||||
"Driver Interface": "驱动接口",
|
||||
"Duplicate tag name: {tag}": "重复的tag名称:{tag}",
|
||||
"EGP": "",
|
||||
"ENTRYPOINT": "运行命令(ENTRYPOINT)",
|
||||
"ESP": "",
|
||||
"Each instance belongs to at least one security group, which needs to be specified when it is created. Instances in the same security group can communicate with each other on the network, and instances in different security groups are disconnected from the internal network by default.": "每个云主机至少属于一个安全组,在创建的时候就需要指定。同一安全组内的云主机之间网络互通,不同安全组的云主机之间默认内网不通。",
|
||||
"Each new connection request is assigned to the next server in order, and all requests are finally divided equally among all servers. Commonly used for short connection services, such as HTTP services.": "按顺序把每个新的连接请求分配给下一个服务器,最终把所有请求平分给所有的服务器。常用于短连接服务,例如HTTP等服务。",
|
||||
@ -848,6 +854,7 @@
|
||||
"Enable DHCP": "DHCP 已启用",
|
||||
"Enable Domain": "启用域",
|
||||
"Enable Floating IP": "使用浮动IP",
|
||||
"Enable Health Check": "启用健康检查",
|
||||
"Enable HealthMonitor": "启用健康检查",
|
||||
"Enable Load Balancer": "启用负载均衡",
|
||||
"Enable Neutron Agent": "启用网络服务",
|
||||
@ -857,6 +864,7 @@
|
||||
"Enable Service": "启用服务",
|
||||
"Enable User": "启用用户",
|
||||
"Enable auto heal": "启用自动修复",
|
||||
"Enable auto remove": "启用自动删除",
|
||||
"Enable compute host": "启用计算节点",
|
||||
"Enable interactive mode": "启用交互模式",
|
||||
"Enabled": "启用",
|
||||
@ -901,6 +909,7 @@
|
||||
"Expires At": "到期时间",
|
||||
"Export Location": "导入位置",
|
||||
"Export Locations": "导入位置",
|
||||
"Exposed Ports": "服务端口",
|
||||
"Extend Root Volume": "扩容根硬盘",
|
||||
"Extend Share": "扩容共享",
|
||||
"Extend Volume": "扩容云硬盘",
|
||||
@ -1016,6 +1025,7 @@
|
||||
"Gibraltar": "直布罗陀",
|
||||
"Given IP": "指定IP",
|
||||
"Glance": "",
|
||||
"Glance Image": "本地镜像(Glance)",
|
||||
"Global Setting": "平台配置",
|
||||
"GlusterFS": "",
|
||||
"Grant Databases Access": "设置数据库访问",
|
||||
@ -1037,6 +1047,10 @@
|
||||
"Hard Reboot": "硬重启",
|
||||
"Hard Rebooting": "硬重启中",
|
||||
"Hash": "Hash",
|
||||
"Health Check CMD": "健康检查命令",
|
||||
"Health Check Interval": "健康检查间隔时间",
|
||||
"Health Check Retries": "健康检查重试次数",
|
||||
"Health Check Timeout": "健康检查超时时间",
|
||||
"Health Checking Log": "健康检查日志",
|
||||
"Health Monitor Delay": "检查间隔(秒)",
|
||||
"Health Monitor Detail": "健康检查器详情",
|
||||
@ -1124,6 +1138,7 @@
|
||||
"If OS is Linux, system will reset root password, if OS is Windows, system will reset Administrator password.": "如果操作系统是Linux,系统会修改root用户密码,如果是Windows,系统会修改Administrator用户密码。",
|
||||
"If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?": "若有云主机正在使用此 flavor,删除会导致云主机的 flavor 数据缺失,确定删除 {name} ?",
|
||||
"If checked, the network will be enable.": "如果选中,那么网络将被启用。",
|
||||
"If exposed port is specified, this parameter will be ignored.": "如果指定了服务端口,这个参数将被忽略。",
|
||||
"If it is an SNI type certificate, a domain name needs to be specified": "如果是 SNI 类型证书,需指定域名",
|
||||
"If it’s not set, the value of this in template will be used.": "如果不设置,将使用模板的值",
|
||||
"If no gateway is specified, the first IP address will be defaulted.": "如果不指定网关IP,默认是第一个地址。",
|
||||
@ -1133,6 +1148,7 @@
|
||||
"If the capacity of the disk is large,the type modify operation may takes several hours. Please be cautious.": "如果云硬盘容量较大,修改云硬盘类型可能需要花费几个小时,请您谨慎操作。",
|
||||
"If the value is set to 0, it means unlimited": "如果值为0,则表示无限制",
|
||||
"If the volume associated with the snapshot has changed the volume type, please modify this option manually; if the volume associated with the snapshot keeps the volume type unchanged, please ignore this option. (no need to change).": "若快照关联的云硬盘修改过云硬盘类型,请手动修改此选项;若快照关联的云硬盘保持云硬盘类型不变,请忽略此选项(不需要做变更)。",
|
||||
"If this parameter is specified, Zun will create a security group with a set of rules to open the ports that should be exposed, and associate the security group to the container.": "如果指定了这个参数,Zun 会创建一个安全组,里面有一组规则来开放应该暴露的端口,并将安全组关联到容器上。",
|
||||
"If you are not authorized to access any project, or if the project you are involved in has been deleted or disabled, contact the platform administrator to reassign the project": "您未被授权访问任何项目,或您参与中的项目已被删除或禁用,可联系平台管理员重新分配项目",
|
||||
"If you are not sure which authentication method to use, please contact your administrator.": "如果您不确定使用哪种认证方式,请联系管理员。",
|
||||
"If you choose a port which subnet is different from the subnet of LB, please ensure connectivity between the two.": "如果你选择了和LB子网不同的网卡,请确保两者的连通性。",
|
||||
@ -1143,11 +1159,10 @@
|
||||
"Image & OS": "镜像和操作系统",
|
||||
"Image Backup": "镜像备份",
|
||||
"Image Detail": "镜像详情",
|
||||
"Image Driver": "镜像驱动程序",
|
||||
"Image Driver": "镜像来源",
|
||||
"Image Info": "镜像信息",
|
||||
"Image Name": "镜像名称",
|
||||
"Image Pending Upload": "镜像待上传",
|
||||
"Image Pull Policy": "镜像拉取策略",
|
||||
"Image Size": "镜像大小",
|
||||
"Image Snapshot Pending": "镜像快照等待上传",
|
||||
"Image Uploading": "镜像上传中",
|
||||
@ -1432,6 +1447,7 @@
|
||||
"Max Retry": "最大重试次数",
|
||||
"Max connect": "最大连接数",
|
||||
"Maximum interval time for each health check response": "每个健康检查响应的最大间隔时间",
|
||||
"Maximum time to allow one check to run in seconds": "允许一次检查运行的最长时间(以秒为单位)",
|
||||
"Mayotte": "马约特",
|
||||
"Mem": "内存",
|
||||
"Member Count": "成员数量",
|
||||
@ -1727,14 +1743,19 @@
|
||||
"Please input cipher": "请输入cipher",
|
||||
"Please input cluster name": "请输入集群名称",
|
||||
"Please input cluster template name": "请输入集群模板名称",
|
||||
"Please input complete data": "请输入完整的数据",
|
||||
"Please input container name": "请输入容器名称",
|
||||
"Please input file name": "请输入文件名称",
|
||||
"Please input image": "请输入镜像",
|
||||
"Please input ipv4": "请输入IPV4",
|
||||
"Please input ipv6": "请输入IPV6",
|
||||
"Please input key": "请输入键",
|
||||
"Please input key and value": "请输入键和值",
|
||||
"Please input key size": "请输入密钥大小",
|
||||
"Please input metadata": "请输入元数据",
|
||||
"Please input name": "请输入名称",
|
||||
"Please input or load Template from a file": "请输入或者从文件加载模板",
|
||||
"Please input port and protocol": "请输入端口和协议",
|
||||
"Please input prefix": "请输入前缀",
|
||||
"Please input protocol number if it absent in select list.": "如果选择列表中没有,请输入协议号。",
|
||||
"Please input provider": "请输入提供者",
|
||||
@ -1759,6 +1780,7 @@
|
||||
"Please select a subnet!": "请选择子网!",
|
||||
"Please select a type!": "请选择类型!",
|
||||
"Please select availability zone": "请选择可用域",
|
||||
"Please select image driver": "请选择镜像来源",
|
||||
"Please select item!": "请选择一个条目!",
|
||||
"Please select key": "请选择一个键",
|
||||
"Please select login type!": "请选择登录方式!",
|
||||
@ -2301,6 +2323,7 @@
|
||||
"The disk size in GiB for per container": "以 GiB 为单位的容器磁盘大小",
|
||||
"The domain name can only be composed of letters, numbers, dashes, in A dash cannot be at the beginning or end, and a single string cannot exceed more than 63 characters, separated by dots; At most can support 30 domain names, separated by commas;The length of a single domain name does not exceed 100 characters, and the total length degree does not exceed 1024 characters.": "域名只能由字母,数字,中划线组成,中划线不能在开头或末尾,单个字符串不超过63个字符,字符串间以点分隔;最多可支持30个域名,域名间以英文逗号分隔;单个域名长度不超过100个字符,且总长度不超过1024个字符。",
|
||||
"The entire inspection process takes 5 to 10 minutes, so you need to be patient. After the registration is completed, the node configuration status will return to the manageable status.": "检查的整个过程需要耗费 5 到 10 分钟时间,您需要耐心等待。在完成注册后,节点配置状态会重新回到可管理状态。",
|
||||
"The entrypoint which overwrites the default ENTRYPOINT of the image": "它将覆盖镜像默认的入口点",
|
||||
"The feasible configuration of cloud-init or cloudbase-init service in the image is not synced to image's properties, so the Login Name is unknown.": "镜像中的cloud-init或cloudbase-init服务的预制配置未同步至镜像属性, 登录名未知",
|
||||
"The file with the same name will be overwritten.": "对同名文件将会进行文件覆盖操作。",
|
||||
"The floating IP configured with port forwardings cannot be bound": "不允许绑定配置了端口转发的浮动IP",
|
||||
@ -2329,6 +2352,7 @@
|
||||
"The name of the physical network to which a port is connected": "端口连接到的物理网络的名称",
|
||||
"The name should contain letter or number, the length is 1 to 16, characters can only contain \"0-9, a-z, A-Z, -, _.\"": "名称应包含字母或数字,长度为 1 到 16,且字符只能包含“0-9、a-z、A-Z、-、_”。",
|
||||
"The name should contain letter or number, the length is 2 to 64, characters can only contain \"0-9, a-z, A-Z, -, _.\"": "名称应包含字母或数字,长度为 2 到 64,且字符只能包含“0-9、a-z、A-Z、-、_”。",
|
||||
"The name should start with letter or number, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "名称应以字母或数字开头,且只包含“0-9, a-z, A-Z, -, _, .”。",
|
||||
"The name should start with upper letter or lower letter, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].:^\".": "名称应以大写字母或小写字母开头,最长为128字符,且只包含“0-9, a-z, A-Z, \"'-_()[].:^”。",
|
||||
"The name should start with upper letter or lower letter, characters can only contain \"0-9, a-z, A-Z, -, _, .\"": "名称应以大写字母或小写字母开头,且字符只能包含“0-9、a-z、A-Z、-、_、.”。",
|
||||
"The name should start with upper letter, lower letter or chinese, and be a string of 1 to 128, characters can only contain \"0-9, a-z, A-Z, \"-'_()[].\".": "名称应以大写字母,小写字母或中文开头,最长为128字符,且只包含“0-9, a-z, A-Z, \"'-_()[].”。",
|
||||
@ -2389,6 +2413,7 @@
|
||||
"This will delete all child objects of the load balancer.": "这会删除所有LB下的资源",
|
||||
"Threads Activity Trends": "线程活动趋势",
|
||||
"Time Interval: ": "时间间隔:",
|
||||
"Time between running the check in seconds": "运行检查之间的时间(以秒为单位)",
|
||||
"Timeout(Minute)": "创建超时(分钟)",
|
||||
"Timeout(s)": "检查超时时间(秒)",
|
||||
"To open": "去开通",
|
||||
|
@ -21,7 +21,7 @@ import { isEmpty } from 'lodash';
|
||||
|
||||
export class BaseDetail extends Base {
|
||||
get leftCards() {
|
||||
const cards = [this.baseInfoCard, this.miscellaneousCard];
|
||||
const cards = [this.baseInfoCard, this.otherCard];
|
||||
const { stats } = this.detailData;
|
||||
if (!isEmpty(stats)) {
|
||||
cards.push(this.statsCard);
|
||||
@ -35,6 +35,14 @@ export class BaseDetail extends Base {
|
||||
|
||||
get baseInfoCard() {
|
||||
const options = [
|
||||
{
|
||||
label: t('Image'),
|
||||
dataIndex: 'image',
|
||||
},
|
||||
{
|
||||
label: t('Image Driver'),
|
||||
dataIndex: 'image_driver',
|
||||
},
|
||||
{
|
||||
label: t('Status Detail'),
|
||||
dataIndex: 'status_detail',
|
||||
@ -48,11 +56,6 @@ export class BaseDetail extends Base {
|
||||
label: t('Task State'),
|
||||
dataIndex: 'task_state',
|
||||
},
|
||||
{
|
||||
label: t('Command'),
|
||||
dataIndex: 'command',
|
||||
render: stringifyContent,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
@ -61,12 +64,30 @@ export class BaseDetail extends Base {
|
||||
};
|
||||
}
|
||||
|
||||
get miscellaneousCard() {
|
||||
get otherCard() {
|
||||
const options = [
|
||||
{
|
||||
label: t('Host'),
|
||||
dataIndex: 'host',
|
||||
},
|
||||
{
|
||||
label: t('Hostname'),
|
||||
dataIndex: 'hostname',
|
||||
},
|
||||
{
|
||||
label: t('Runtime'),
|
||||
dataIndex: 'runtime',
|
||||
},
|
||||
{
|
||||
label: t('CMD'),
|
||||
dataIndex: 'command',
|
||||
render: stringifyContent,
|
||||
},
|
||||
{
|
||||
label: t('ENTRYPOINT'),
|
||||
dataIndex: 'entrypoint',
|
||||
render: stringifyContent,
|
||||
},
|
||||
{
|
||||
label: t('Workdir'),
|
||||
dataIndex: 'workdir',
|
||||
@ -77,40 +98,20 @@ export class BaseDetail extends Base {
|
||||
render: stringifyContent,
|
||||
},
|
||||
{
|
||||
label: t('Interactive'),
|
||||
dataIndex: 'interactive',
|
||||
valueRender: 'yesNo',
|
||||
label: t('Labels'),
|
||||
dataIndex: 'labels',
|
||||
render: stringifyContent,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
title: t('Miscellaneous'),
|
||||
title: t('Others'),
|
||||
options,
|
||||
};
|
||||
}
|
||||
|
||||
get specCard() {
|
||||
const options = [
|
||||
{
|
||||
label: t('Image'),
|
||||
dataIndex: 'image',
|
||||
},
|
||||
{
|
||||
label: t('Image Driver'),
|
||||
dataIndex: 'image_driver',
|
||||
},
|
||||
{
|
||||
label: t('Image Pull Policy'),
|
||||
dataIndex: 'image_pull_policy',
|
||||
},
|
||||
{
|
||||
label: t('Hostname'),
|
||||
dataIndex: 'hostname',
|
||||
},
|
||||
{
|
||||
label: t('Runtime'),
|
||||
dataIndex: 'runtime',
|
||||
},
|
||||
{
|
||||
label: t('CPU (Core)'),
|
||||
dataIndex: 'cpu',
|
||||
@ -126,15 +127,63 @@ export class BaseDetail extends Base {
|
||||
{
|
||||
label: t('Restart Policy'),
|
||||
dataIndex: 'restart_policy',
|
||||
render: stringifyContent,
|
||||
render: (value) => {
|
||||
if (isEmpty(value)) {
|
||||
return '-';
|
||||
}
|
||||
const { Name, MaximumRetryCount } = value;
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{t('Name')}: {Name}
|
||||
</p>
|
||||
<p>
|
||||
{t('Max Retry')}: {MaximumRetryCount}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('Auto Remove'),
|
||||
dataIndex: 'auto_remove',
|
||||
valueRender: 'yesNo',
|
||||
},
|
||||
{
|
||||
label: t('Auto Heal'),
|
||||
dataIndex: 'auto_heal',
|
||||
valueRender: 'yesNo',
|
||||
},
|
||||
{
|
||||
label: t('Interactive'),
|
||||
dataIndex: 'interactive',
|
||||
valueRender: 'yesNo',
|
||||
},
|
||||
{
|
||||
label: t('Enable Health Check'),
|
||||
dataIndex: 'healthcheck',
|
||||
render: (value) => {
|
||||
if (isEmpty(value)) {
|
||||
return t('No');
|
||||
}
|
||||
const { interval, retries, test, timeout } = value;
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{t('Health Check CMD')}: {test}
|
||||
</p>
|
||||
<p>
|
||||
{t('Health Check Interval')}: {interval} s
|
||||
</p>
|
||||
<p>
|
||||
{t('Health Check Retries')}: {retries}
|
||||
</p>
|
||||
<p>
|
||||
{t('Health Check Timeout')}: {timeout} s
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('Addresses'),
|
||||
|
@ -13,11 +13,8 @@
|
||||
import Base from 'components/Form';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import globalImageStore from 'src/stores/glance/image';
|
||||
import {
|
||||
getImageColumns,
|
||||
getImageSystemTabs,
|
||||
getImageOS,
|
||||
} from 'resources/glance/image';
|
||||
import { getImageColumns } from 'resources/glance/image';
|
||||
import { toJS } from 'mobx';
|
||||
|
||||
export class StepInfo extends Base {
|
||||
init() {
|
||||
@ -34,42 +31,76 @@ export class StepInfo extends Base {
|
||||
|
||||
async getImageList() {
|
||||
await globalImageStore.fetchList();
|
||||
this.updateDefaultValue();
|
||||
}
|
||||
|
||||
get imageList() {
|
||||
const { imageTab } = this.state;
|
||||
return (globalImageStore.list.data || [])
|
||||
.filter((it) => it.owner === this.currentProjectId)
|
||||
.filter((it) => getImageOS(it) === imageTab);
|
||||
return toJS(globalImageStore.list.data || []).filter(
|
||||
(it) => it.container_format === 'docker'
|
||||
);
|
||||
}
|
||||
|
||||
get imageColumns() {
|
||||
return getImageColumns(this);
|
||||
return getImageColumns(this).filter(
|
||||
(it) => !['project_name', 'owner'].includes(it.dataIndex)
|
||||
);
|
||||
}
|
||||
|
||||
get systemTabs() {
|
||||
const imageTabs = getImageSystemTabs();
|
||||
return imageTabs;
|
||||
}
|
||||
|
||||
onImageTabChange = (value) => {
|
||||
this.setState({
|
||||
imageTab: value,
|
||||
});
|
||||
};
|
||||
|
||||
get formItems() {
|
||||
const { imageDriver } = this.state;
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'name',
|
||||
label: t('Container Name'),
|
||||
type: 'input',
|
||||
placeholder: t('Container Name'),
|
||||
placeholder: t('Please input container name'),
|
||||
required: true,
|
||||
validator: (rule, value) => {
|
||||
const pattern = /^[a-zA-Z0-9][a-zA-Z0-9_.-]+$/;
|
||||
if (!pattern.test(value)) {
|
||||
return Promise.reject(
|
||||
value
|
||||
? t(
|
||||
'The name should start with letter or number, characters can only contain "0-9, a-z, A-Z, -, _, ."'
|
||||
)
|
||||
: ''
|
||||
);
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'image_driver',
|
||||
label: t('Image Driver'),
|
||||
placeholder: t('Please select image driver'),
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: t('Docker Hub'),
|
||||
value: 'docker',
|
||||
},
|
||||
{
|
||||
label: t('Glance Image'),
|
||||
value: 'glance',
|
||||
},
|
||||
],
|
||||
onChange: (value) => {
|
||||
this.setState({
|
||||
imageDriver: value,
|
||||
});
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'images',
|
||||
name: 'image',
|
||||
label: t('Image'),
|
||||
type: 'input',
|
||||
placeholder: t('Please input image'),
|
||||
required: true,
|
||||
display: imageDriver === 'docker',
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
label: t('Image'),
|
||||
type: 'select-table',
|
||||
data: this.imageList,
|
||||
@ -82,32 +113,7 @@ export class StepInfo extends Base {
|
||||
},
|
||||
],
|
||||
columns: this.imageColumns,
|
||||
tabs: this.systemTabs,
|
||||
defaultTabValue: this.systemTabs[0].value,
|
||||
onTabChange: this.onImageTabChange,
|
||||
},
|
||||
{
|
||||
name: 'image_driver',
|
||||
label: t('Image Driver'),
|
||||
placeholder: t('Image Driver'),
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: t('Docker'),
|
||||
value: 'docker',
|
||||
},
|
||||
{
|
||||
label: t('Glance'),
|
||||
value: 'glance',
|
||||
},
|
||||
],
|
||||
allowClear: true,
|
||||
},
|
||||
{
|
||||
name: 'command',
|
||||
label: t('Command'),
|
||||
type: 'input',
|
||||
placeholder: t('A command that will be sent to the container'),
|
||||
display: imageDriver === 'glance',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -36,19 +36,11 @@ export class StepNetworks extends Base {
|
||||
return t('Networks');
|
||||
}
|
||||
|
||||
get nameForStateUpdate() {
|
||||
return ['networks'];
|
||||
}
|
||||
|
||||
get defaultValue() {
|
||||
const data = {
|
||||
networks: [],
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
const { networks } = this.state;
|
||||
const { networks = [] } = this.state;
|
||||
const {
|
||||
context: { exposedPorts = [] },
|
||||
} = this.props;
|
||||
|
||||
return [
|
||||
{
|
||||
@ -82,7 +74,7 @@ export class StepNetworks extends Base {
|
||||
backendPageStore: this.securityGroupStore,
|
||||
extraParams: { project_id: this.currentProjectId },
|
||||
isMulti: true,
|
||||
hidden: !networks || !networks.length,
|
||||
hidden: exposedPorts.length || !networks.length,
|
||||
header: (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
{t(
|
||||
@ -97,6 +89,7 @@ export class StepNetworks extends Base {
|
||||
),
|
||||
filterParams: securityGroupFilter,
|
||||
columns: securityGroupColumns,
|
||||
tip: t('If exposed port is specified, this parameter will be ignored.'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -14,17 +14,57 @@ import Base from 'components/Form';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import KeyValueInput from 'components/FormItem/KeyValueInput';
|
||||
|
||||
export class StepMiscellaneous extends Base {
|
||||
export class StepOthers extends Base {
|
||||
get title() {
|
||||
return t('Miscellaneous');
|
||||
return t('Others');
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('Miscellaneous');
|
||||
return t('Others');
|
||||
}
|
||||
|
||||
keyValueValidator = (rule, value) => {
|
||||
const ifHaveEmpty = (value || []).some((it) => {
|
||||
const { value: innerValue } = it;
|
||||
if (innerValue?.key && innerValue?.value) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (ifHaveEmpty) {
|
||||
return Promise.reject(new Error(t('Please input key and value')));
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
get formItems() {
|
||||
return [
|
||||
{
|
||||
name: 'hostname',
|
||||
label: t('Hostname'),
|
||||
type: 'input',
|
||||
placeholder: t('The host name of this container'),
|
||||
},
|
||||
{
|
||||
name: 'runtime',
|
||||
label: t('Runtime'),
|
||||
type: 'input',
|
||||
placeholder: t('The container runtime tool to create container with'),
|
||||
},
|
||||
{
|
||||
name: 'command',
|
||||
label: t('CMD'),
|
||||
type: 'input',
|
||||
placeholder: t('A command that will be sent to the container'),
|
||||
},
|
||||
{
|
||||
name: 'entrypoint',
|
||||
label: t('ENTRYPOINT'),
|
||||
type: 'input',
|
||||
extra: t(
|
||||
'The entrypoint which overwrites the default ENTRYPOINT of the image'
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'workdir',
|
||||
label: t('Working Directory'),
|
||||
@ -37,24 +77,7 @@ export class StepMiscellaneous extends Base {
|
||||
type: 'add-select',
|
||||
itemComponent: KeyValueInput,
|
||||
addText: t('Add Environment Variable'),
|
||||
},
|
||||
{
|
||||
name: 'interactive',
|
||||
label: t('Enable interactive mode'),
|
||||
type: 'check',
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
name: 'labels',
|
||||
label: t('Labels'),
|
||||
type: 'add-select',
|
||||
itemComponent: KeyValueInput,
|
||||
addText: t('Add Label'),
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
validator: this.keyValueValidator,
|
||||
},
|
||||
{
|
||||
name: 'hints',
|
||||
@ -62,9 +85,18 @@ export class StepMiscellaneous extends Base {
|
||||
type: 'add-select',
|
||||
itemComponent: KeyValueInput,
|
||||
addText: t('Add scheduler hints'),
|
||||
validator: this.keyValueValidator,
|
||||
},
|
||||
{
|
||||
name: 'labels',
|
||||
label: t('Labels'),
|
||||
type: 'add-select',
|
||||
itemComponent: KeyValueInput,
|
||||
addText: t('Add Label'),
|
||||
validator: this.keyValueValidator,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(StepMiscellaneous));
|
||||
export default inject('rootStore')(observer(StepOthers));
|
@ -13,6 +13,7 @@
|
||||
import Base from 'components/Form';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import globalAvailabilityZoneStore from 'stores/nova/zone';
|
||||
import ExposedPorts from '../../../components/ExposedPorts';
|
||||
|
||||
export class StepSpec extends Base {
|
||||
init() {
|
||||
@ -41,21 +42,24 @@ export class StepSpec extends Base {
|
||||
}));
|
||||
}
|
||||
|
||||
exposedPortValidator = (rule, value) => {
|
||||
const ifHaveEmpty = (value || []).some((it) => {
|
||||
const { value: innerValue } = it;
|
||||
if (innerValue?.port && innerValue?.protocol) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (ifHaveEmpty) {
|
||||
return Promise.reject(new Error(t('Please input port and protocol')));
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
get formItems() {
|
||||
const { disableRetry } = this.state;
|
||||
const { disableRetry, healthcheck } = this.state;
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'hostname',
|
||||
label: t('Hostname'),
|
||||
type: 'input',
|
||||
placeholder: t('The host name of this container'),
|
||||
},
|
||||
{
|
||||
name: 'runtime',
|
||||
label: t('Runtime'),
|
||||
type: 'input',
|
||||
placeholder: t('The container runtime tool to create container with'),
|
||||
},
|
||||
{
|
||||
name: 'cpu',
|
||||
label: t('CPU (Core)'),
|
||||
@ -135,6 +139,77 @@ export class StepSpec extends Base {
|
||||
label: t('Enable auto heal'),
|
||||
type: 'check',
|
||||
},
|
||||
{
|
||||
name: 'auto_remove',
|
||||
label: t('Enable auto remove'),
|
||||
type: 'check',
|
||||
},
|
||||
{
|
||||
name: 'interactive',
|
||||
label: t('Enable interactive mode'),
|
||||
type: 'check',
|
||||
},
|
||||
{
|
||||
name: 'healthcheck',
|
||||
label: t('Enable Health Check'),
|
||||
type: 'check',
|
||||
onChange: (value) => {
|
||||
this.setState({
|
||||
healthcheck: value,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'healthcheck_cmd',
|
||||
label: t('Health Check CMD'),
|
||||
extra: t('Command to run to check health'),
|
||||
type: 'input',
|
||||
min: 1,
|
||||
required: !!healthcheck,
|
||||
display: !!healthcheck,
|
||||
},
|
||||
{
|
||||
name: 'healthcheck_interval',
|
||||
label: t('Health Check Interval'),
|
||||
extra: t('Time between running the check in seconds'),
|
||||
type: 'input-int',
|
||||
min: 1,
|
||||
required: !!healthcheck,
|
||||
display: !!healthcheck,
|
||||
},
|
||||
{
|
||||
name: 'healthcheck_retries',
|
||||
label: t('Health Check Retries'),
|
||||
extra: t('Consecutive failures needed to report unhealthy'),
|
||||
type: 'input-int',
|
||||
min: 1,
|
||||
required: !!healthcheck,
|
||||
display: !!healthcheck,
|
||||
},
|
||||
{
|
||||
name: 'healthcheck_timeout',
|
||||
label: t('Health Check Timeout'),
|
||||
extra: t('Maximum time to allow one check to run in seconds'),
|
||||
type: 'input-int',
|
||||
min: 1,
|
||||
required: !!healthcheck,
|
||||
display: !!healthcheck,
|
||||
},
|
||||
{
|
||||
name: 'exposedPorts',
|
||||
label: t('Exposed Ports'),
|
||||
type: 'add-select',
|
||||
optionsProtocol: [
|
||||
{ label: t('TCP'), value: 'tcp' },
|
||||
{ label: t('UDP'), value: 'udp' },
|
||||
],
|
||||
itemComponent: ExposedPorts,
|
||||
addText: t('Add Exposed Ports'),
|
||||
validator: this.exposedPortValidator,
|
||||
tip: t(
|
||||
'If this parameter is specified, Zun will create a security group with a set of rules to open the ports that should be exposed, and associate the security group to the container.'
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
import Base from 'components/Form';
|
||||
import { toJS } from 'mobx';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import ZunVolume from 'src/components/FormItem/ZunVolume';
|
||||
import { VolumeStore } from 'src/stores/cinder/volume';
|
||||
import { VolumeStore } from 'stores/cinder/volume';
|
||||
import ZunVolume from '../../../components/ZunVolume';
|
||||
|
||||
export class StepVolumes extends Base {
|
||||
init() {
|
||||
@ -22,7 +23,7 @@ export class StepVolumes extends Base {
|
||||
}
|
||||
|
||||
get volumes() {
|
||||
return (this.volumeStore.list.data || [])
|
||||
return toJS(this.volumeStore.list.data || [])
|
||||
.filter((it) => it.status === 'available')
|
||||
.map((it) => ({
|
||||
value: it.id,
|
||||
@ -32,6 +33,7 @@ export class StepVolumes extends Base {
|
||||
|
||||
async getVolumes() {
|
||||
await this.volumeStore.fetchList();
|
||||
this.updateDefaultValue();
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
@ -46,6 +48,25 @@ export class StepVolumes extends Base {
|
||||
],
|
||||
optionsSource: this.volumes,
|
||||
itemComponent: ZunVolume,
|
||||
validator: (rule, value) => {
|
||||
const ifHaveEmpty = (value || []).some((it) => {
|
||||
const { value: innerValue = {} } = it;
|
||||
if (!innerValue.type) {
|
||||
return true;
|
||||
}
|
||||
if (innerValue.type === 'volume') {
|
||||
return !innerValue.size || !innerValue.destination;
|
||||
}
|
||||
if (innerValue.type === 'bind') {
|
||||
return !innerValue.source || !innerValue.destination;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (ifHaveEmpty) {
|
||||
return Promise.reject(new Error(t('Please input complete data')));
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -15,11 +15,12 @@ import { message as $message } from 'antd';
|
||||
import { StepAction } from 'src/containers/Action';
|
||||
import globalContainersStore from 'stores/zun/containers';
|
||||
import globalProjectStore from 'stores/keystone/project';
|
||||
import { isEmpty, isObject } from 'lodash';
|
||||
import StepInfo from './StepInfo';
|
||||
import StepSpec from './StepSpec';
|
||||
import StepVolumes from './StepVolumes';
|
||||
import StepNetworks from './StepNetworks';
|
||||
import StepMiscellaneous from './StepMiscellaneous';
|
||||
import StepOthers from './StepOthers';
|
||||
|
||||
export class StepCreate extends StepAction {
|
||||
init() {
|
||||
@ -75,8 +76,8 @@ export class StepCreate extends StepAction {
|
||||
component: StepNetworks,
|
||||
},
|
||||
{
|
||||
title: t('Miscellaneous'),
|
||||
component: StepMiscellaneous,
|
||||
title: t('Others'),
|
||||
component: StepOthers,
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -222,99 +223,144 @@ export class StepCreate extends StepAction {
|
||||
|
||||
onSubmit = (values) => {
|
||||
const {
|
||||
exposedPorts,
|
||||
environmentVariables,
|
||||
labels,
|
||||
mounts,
|
||||
images,
|
||||
image,
|
||||
exitPolicy,
|
||||
maxRetry,
|
||||
networks,
|
||||
ports,
|
||||
hints,
|
||||
securityGroup,
|
||||
healthcheck,
|
||||
healthcheck_cmd,
|
||||
healthcheck_interval,
|
||||
healthcheck_retries,
|
||||
healthcheck_timeout,
|
||||
command,
|
||||
entrypoint,
|
||||
...rest
|
||||
} = values;
|
||||
|
||||
const requestEnvironment = {};
|
||||
const requestLabels = {};
|
||||
const requestVolumes = [];
|
||||
const requestHints = {};
|
||||
const body = {
|
||||
...rest,
|
||||
};
|
||||
|
||||
const requestExposedPorts = {};
|
||||
const nets = [];
|
||||
const securityGroups = [];
|
||||
|
||||
if (environmentVariables) {
|
||||
environmentVariables.forEach((item) => {
|
||||
const labelKey = item.value.key.toLowerCase().trim();
|
||||
const labelValue = item.value.value.toLowerCase().trim();
|
||||
requestEnvironment[labelKey] = labelValue;
|
||||
if (exposedPorts && exposedPorts.length) {
|
||||
exposedPorts.forEach((item) => {
|
||||
const key = `${item.value.port}/${item.value.protocol}`;
|
||||
requestExposedPorts[key] = {};
|
||||
});
|
||||
body.exposed_ports = requestExposedPorts;
|
||||
}
|
||||
|
||||
if (labels) {
|
||||
labels.forEach((item) => {
|
||||
const key = item.value.key.toLowerCase().trim();
|
||||
const value = item.value.value.toLowerCase().trim();
|
||||
requestLabels[key] = value;
|
||||
});
|
||||
if (environmentVariables && environmentVariables.length) {
|
||||
const requestEnvironment = environmentVariables.reduce(
|
||||
(result, current) => {
|
||||
const labelKey = current.value.key;
|
||||
const labelValue = current.value.value;
|
||||
result[labelKey] = labelValue;
|
||||
return result;
|
||||
},
|
||||
{}
|
||||
);
|
||||
body.environment = requestEnvironment;
|
||||
}
|
||||
|
||||
if (mounts) {
|
||||
mounts.forEach((item) => {
|
||||
const { type, source, size, destination, isNewVolume } = item.value;
|
||||
if (labels && labels.length) {
|
||||
const requestLabels = labels.reduce((result, current) => {
|
||||
const { key } = current.value;
|
||||
const { value } = current.value;
|
||||
result[key] = value;
|
||||
return result;
|
||||
}, {});
|
||||
body.labels = requestLabels;
|
||||
}
|
||||
|
||||
if (mounts && mounts.length) {
|
||||
const requestVolumes = mounts.reduce((result, current) => {
|
||||
const { type, source, size, destination, isNewVolume } = current.value;
|
||||
if (isNewVolume) {
|
||||
requestVolumes.push({
|
||||
result.push({
|
||||
type,
|
||||
size,
|
||||
destination,
|
||||
});
|
||||
} else {
|
||||
requestVolumes.push({
|
||||
result.push({
|
||||
type,
|
||||
source,
|
||||
destination,
|
||||
});
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}, []);
|
||||
body.mounts = requestVolumes;
|
||||
}
|
||||
|
||||
if (networks) {
|
||||
(networks.selectedRowKeys || []).forEach((it) => {
|
||||
if (networks && networks.selectedRowKeys.length) {
|
||||
networks.selectedRowKeys.forEach((it) => {
|
||||
nets.push({ network: it });
|
||||
});
|
||||
body.nets = nets;
|
||||
}
|
||||
|
||||
if (ports) {
|
||||
(ports.selectedRowKeys || []).forEach((it) => {
|
||||
if (ports && ports.selectedRowKeys.length) {
|
||||
ports.selectedRowKeys.forEach((it) => {
|
||||
nets.push({ port: it });
|
||||
});
|
||||
body.nets = nets;
|
||||
}
|
||||
|
||||
if (securityGroup) {
|
||||
(securityGroup.selectedRows || []).forEach((it) => {
|
||||
securityGroups.push(it.name);
|
||||
});
|
||||
if (hints && hints.length) {
|
||||
const requestHints = hints.reduce((result, current) => {
|
||||
const { key } = current.value;
|
||||
const { value } = current.value;
|
||||
result[key] = value;
|
||||
return result;
|
||||
}, {});
|
||||
body.hints = requestHints;
|
||||
}
|
||||
|
||||
if (hints) {
|
||||
hints.forEach((item) => {
|
||||
const key = item.value.key.toLowerCase().trim();
|
||||
const value = item.value.value.toLowerCase().trim();
|
||||
requestHints[key] = value;
|
||||
});
|
||||
if (
|
||||
securityGroup &&
|
||||
securityGroup.selectedRows.length &&
|
||||
isEmpty(requestExposedPorts)
|
||||
) {
|
||||
const securityGroups = securityGroup.selectedRows.reduce(
|
||||
(result, current) => {
|
||||
result.push(current.name);
|
||||
return result;
|
||||
},
|
||||
[]
|
||||
);
|
||||
body.security_groups = securityGroups;
|
||||
}
|
||||
|
||||
const body = {
|
||||
environment: requestEnvironment,
|
||||
labels: requestLabels,
|
||||
mounts: requestVolumes,
|
||||
hints: requestHints,
|
||||
nets,
|
||||
security_groups: securityGroups,
|
||||
...rest,
|
||||
};
|
||||
if (healthcheck) {
|
||||
body.healthcheck = {
|
||||
cmd: healthcheck_cmd,
|
||||
interval: healthcheck_interval,
|
||||
retries: healthcheck_retries,
|
||||
timeout: healthcheck_timeout,
|
||||
};
|
||||
}
|
||||
|
||||
if (images) {
|
||||
body.image = (images.selectedRows[0] || {}).name;
|
||||
if (command) {
|
||||
body.command = [command];
|
||||
}
|
||||
|
||||
if (entrypoint) {
|
||||
body.entrypoint = [entrypoint];
|
||||
}
|
||||
|
||||
if (image) {
|
||||
body.image = isObject(image) ? (image.selectedRows[0] || {}).name : image;
|
||||
}
|
||||
|
||||
if (exitPolicy) {
|
||||
|
@ -0,0 +1,97 @@
|
||||
// Copyright 2021 99cloud
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import { Select, Row, Col, Form } from 'antd';
|
||||
import PropTypes from 'prop-types';
|
||||
import InputInt from 'components/FormItem/InputInt';
|
||||
import styles from './index.less';
|
||||
|
||||
export default class ExposedPorts extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
value: PropTypes.any,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
port: '',
|
||||
protocol: '',
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
const { port, protocol } = nextProps.value || {};
|
||||
if (port !== prevState.port || protocol !== prevState.protocol) {
|
||||
return {
|
||||
port,
|
||||
protocol,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
onChange = (value) => {
|
||||
const { onChange } = this.props;
|
||||
if (onChange) {
|
||||
onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
onPortChange = (value) => {
|
||||
this.onChange({
|
||||
...this.state,
|
||||
port: value,
|
||||
});
|
||||
};
|
||||
|
||||
onProtocolChange = (value) => {
|
||||
this.onChange({
|
||||
...this.state,
|
||||
protocol: value,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { port, protocol } = this.state;
|
||||
const inputPort = (
|
||||
<InputInt value={port} min={1} onChange={this.onPortChange} />
|
||||
);
|
||||
const selectProtocol = (
|
||||
<Select
|
||||
value={protocol}
|
||||
options={this.props.optionsProtocol}
|
||||
onChange={this.onProtocolChange}
|
||||
className={styles.select}
|
||||
required
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Form.Item className={styles['select-input']}>
|
||||
<Row gutter={8}>
|
||||
<Col span={6}>
|
||||
<span className={styles.label}>{t('Port')}</span>
|
||||
{inputPort}
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<span className={styles.label}>{t('Protocol')}</span>
|
||||
{selectProtocol}
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
}
|
@ -15,8 +15,8 @@
|
||||
import React from 'react';
|
||||
import { Select, Row, Col, Form, Input } from 'antd';
|
||||
import PropTypes from 'prop-types';
|
||||
import InputInt from 'components/FormItem/InputInt';
|
||||
import styles from './index.less';
|
||||
import InputInt from '../InputInt';
|
||||
|
||||
export default class ZunVolume extends React.Component {
|
||||
static propTypes = {
|
||||
@ -120,6 +120,7 @@ export default class ZunVolume extends React.Component {
|
||||
const inputSize = (
|
||||
<InputInt
|
||||
value={size}
|
||||
min={1}
|
||||
onChange={this.onVolumeSizeChange}
|
||||
style={{ maxWidth: '40%' }}
|
||||
/>
|
||||
@ -135,17 +136,17 @@ export default class ZunVolume extends React.Component {
|
||||
);
|
||||
|
||||
return (
|
||||
<Form.Item className={styles['zun-volume']}>
|
||||
<Row gutter={24}>
|
||||
<Col span={8}>
|
||||
<Form.Item className={styles['select-input']}>
|
||||
<Row gutter={8}>
|
||||
<Col span={6}>
|
||||
<span className={styles.label}>{t('Type')}</span>
|
||||
{selectType}
|
||||
</Col>
|
||||
<Col span={8} hidden={isNewVolume}>
|
||||
<Col span={10} hidden={isNewVolume}>
|
||||
<span className={styles.label}>{t('Source')}</span>
|
||||
{selectSource}
|
||||
</Col>
|
||||
<Col span={8} hidden={!isNewVolume}>
|
||||
<Col span={10} hidden={!isNewVolume}>
|
||||
<span className={styles.label}>{t('Size (GiB)')}</span>
|
||||
{inputSize}
|
||||
</Col>
|
@ -1,13 +1,13 @@
|
||||
.zun-volume {
|
||||
.select-input {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.label {
|
||||
margin-right: 10px;
|
||||
max-width: 20%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.select {
|
||||
max-width: 80%;
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ onlyOn(zunServiceEnabled, () => {
|
||||
.should('include', `${listUrl}/create`)
|
||||
.wait(5000)
|
||||
.formInput('containerName', zunContainerName)
|
||||
.formSelect('image_driver')
|
||||
.formInput('image', 'cirros')
|
||||
.clickStepActionNextButton()
|
||||
.wait(2000)
|
||||
|
Loading…
Reference in New Issue
Block a user