Merge "feat: support fwaas v2"

This commit is contained in:
Zuul 2024-05-08 03:59:57 +00:00 committed by Gerrit Code Review
commit de18d3e2a1
47 changed files with 4131 additions and 0 deletions

View File

@ -0,0 +1,12 @@
---
features:
- |
Support Fwaas V2:
* Support firewall group page: a tab list page includes firewall group tab, firewall policy tab and firewall rule tab.
* Support firewall page both in the console and administrator.
* Support firewall quota info in the overview page in the console.
* Support update firewall quota in the project's action in administrator.

View File

@ -89,6 +89,7 @@ export const cinderEndpoint = () => getOriginEndpoint('cinder');
export const manilaEndpoint = () => getOriginEndpoint('manilav2');
export const zunEndpoint = () => getOriginEndpoint('zun');
export const masakariEndpoint = () => getOriginEndpoint('masakari');
export const firewallEndpoint = () => getOriginEndpoint('neutron_firewall');
export const apiVersionMaps = {
nova: {

View File

@ -447,6 +447,43 @@ const renderMenu = (t) => {
},
],
},
{
path: '/network/firewall-admin',
name: t('Firewalls'),
key: 'firewallAdmin',
level: 1,
endpoints: 'neutron_firewall',
children: [
{
path: /^\/network\/firewall-policy-admin\/detail\/[^/]+$/,
name: t('Policy Detail'),
key: 'firewallPolicyDetailAdmin',
level: 2,
routePath: '/network/firewall-policy-admin/detail/:id',
},
{
path: /^\/network\/firewall-admin\/[^/]+\/port\/[^/]+$/,
name: t('Firewall Port'),
key: 'firewallPortDetailAdmin',
level: 2,
routePath: '/network/firewall-admin/:firewallId/port/:portId',
},
{
path: /^\/network\/firewall-admin\/detail\/[^/]+$/,
name: t('Firewall Detail'),
key: 'firewallDetailAdmin',
level: 2,
routePath: '/network/firewall-admin/detail/:id',
},
{
path: /^\/network\/firewall-rule-admin\/detail\/[^/]+$/,
name: t('Rule Detail'),
key: 'firewallRuleDetailAdmin',
level: 2,
routePath: '/network/firewall-rule-admin/detail/:id',
},
],
},
{
path: '/network/dns-admin/zones',
name: t('DNS Zones'),

View File

@ -448,6 +448,75 @@ const renderMenu = (t) => {
},
],
},
{
path: '/network/firewall',
name: t('Firewalls'),
key: 'firewall',
level: 1,
endpoints: 'neutron_firewall',
children: [
{
path: /^\/network\/firewall-policy\/detail\/[^/]+$/,
name: t('Policy Detail'),
key: 'firewallPolicyDetail',
level: 2,
routePath: '/network/firewall-policy/detail/:id',
},
{
path: '/network/firewall-rule/create',
name: t('Create Rule'),
key: 'firewallRuleCreate',
level: 2,
},
{
path: '/network/firewall-policy/add',
name: t('Add Policy'),
key: 'firewallPolicyCreate',
level: 2,
},
{
path: '/network/firewall/create',
name: t('Create Firewall'),
key: 'firewallCreate',
level: 2,
},
{
path: /^\/network\/firewall\/[^/]+\/port\/[^/]+$/,
name: t('Firewall Port'),
key: 'firewallPortDetail',
level: 2,
routePath: '/network/firewall/:firewallId/port/:portId',
},
{
path: /^\/network\/firewall-rule\/edit\/[^/]+$/,
name: t('Rule Edit'),
key: 'firewallRuleEdit',
level: 2,
routePath: '/network/firewall-rule/edit/:id',
},
{
path: /^\/network\/firewall\/detail\/[^/]+$/,
name: t('Firewall Detail'),
key: 'firewallDetail',
level: 2,
routePath: '/network/firewall/detail/:id',
},
{
path: /^\/network\/firewall-rule\/detail\/[^/]+$/,
name: t('Rule Detail'),
key: 'firewallRuleDetail',
level: 2,
routePath: '/network/firewall-rule/detail/:id',
},
{
path: /^\/network\/firewall-policy\/edit\/[^/]+$/,
name: t('Policy Edit'),
key: 'firewallPolicyEdit',
level: 2,
routePath: '/network/firewall-policy/edit/:id',
},
],
},
{
path: '/network/dns/zones',
name: t('DNS Zones'),

View File

@ -34,6 +34,7 @@
"A dynamic scheduling algorithm that estimates the server load based on the number of currently active connections. The system allocates new connection requests to the server with the least number of current connections. Commonly used for long connection services, such as database connections and other services.": "A dynamic scheduling algorithm that estimates the server load based on the number of currently active connections. The system allocates new connection requests to the server with the least number of current connections. Commonly used for long connection services, such as database connections and other services.",
"A host aggregate can be associated with at most one AZ. Once the association is established, the AZ cannot be disassociated.": "A host aggregate can be associated with at most one AZ. Once the association is established, the AZ cannot be disassociated.",
"A public container will allow anyone to use the objects in your container through a public URL.": "A public container will allow anyone to use the objects in your container through a public URL.",
"A rule specified before insertion or after insertion a rule. If both are not specified, the new rule is inserted as the first rule of the policy.": "A rule specified before insertion or after insertion a rule. If both are not specified, the new rule is inserted as the first rule of the policy.",
"A snapshot is an image which preserves the disk state of a running instance, which can be used to start a new instance.": "A snapshot is an image which preserves the disk state of a running instance, which can be used to start a new instance.",
"A template is a YAML file that contains configuration information, please enter the correct format.": "A template is a YAML file that contains configuration information, please enter the correct format.",
"A template is a YAML file that contains configuration information.": "A template is a YAML file that contains configuration information.",
@ -41,6 +42,7 @@
"ADOPT COMPLETE": "ADOPT COMPLETE",
"AH": "AH",
"AKI - Amazon kernel image format": "AKI - Amazon kernel image format",
"ALLOW": "ALLOW",
"AMI - Amazon server image format": "AMI - Amazon server image format",
"ANY": "ANY",
"API Address": "API Address",
@ -78,6 +80,7 @@
"Add Metadata": "Add Metadata",
"Add NUMA Node": "Add NUMA Node",
"Add Network": "Add Network",
"Add Policy": "Add Policy",
"Add Property": "Add Property",
"Add Router": "Add Router",
"Add Virtual LAN": "Add Virtual LAN",
@ -174,6 +177,7 @@
"Associate Floating IP": "Associate Floating IP",
"Associate IP": "Associate IP",
"Associate Network": "Associate Network",
"Associated Ports": "Associated Ports",
"Associated QoS Spec ID": "Associated QoS Spec ID",
"Associated QoS Spec ID/Name": "Associated QoS Spec ID/Name",
"Associated Resource": "Associated Resource",
@ -193,6 +197,7 @@
"Attaching": "Attaching",
"Attachments Info": "Attachments Info",
"Attributes": "Attributes",
"Audited": "Audited",
"Australia": "Australia",
"Austria": "Austria",
"Auth Algorithm": "Auth Algorithm",
@ -513,6 +518,8 @@
"Create Encryption": "Create Encryption",
"Create Extra Spec": "Create Extra Spec",
"Create Failed": "Create Failed",
"Create Firewall": "Create Firewall",
"Create Firewall Policy": "Create Firewall Policy",
"Create Flavor": "Create Flavor",
"Create Folder": "Create Folder",
"Create Host Aggregate": "Create Host Aggregate",
@ -528,6 +535,7 @@
"Create Network": "Create Network",
"Create New Network": "Create New Network",
"Create Node": "Create Node",
"Create Policy": "Create Policy",
"Create Port": "Create Port",
"Create Port Forwarding": "Create Port Forwarding",
"Create Port Group": "Create Port Group",
@ -567,6 +575,7 @@
"Create Volume Type": "Create Volume Type",
"Create Zone": "Create Zone",
"Create a full backup, the system will automatically create a new backup chain, the full backup name is the backup chain name; Create an incremental backup, the system will automatically create an incremental backup under the newly created backup chain.": "Create a full backup, the system will automatically create a new backup chain, the full backup name is the backup chain name; Create an incremental backup, the system will automatically create an incremental backup under the newly created backup chain.",
"Create firewall": "Create firewall",
"Create host aggregate": "Create host aggregate",
"Create image": "Create image",
"Create instance": "Create instance",
@ -604,6 +613,7 @@
"Current Project Networks": "Current Project Networks",
"Current Project QoS Policies": "Current Project QoS Policies",
"Current QoS policy name": "Current QoS policy name",
"Current Rules": "Current Rules",
"Current Status": "Current Status",
"Current Storage Backend": "Current Storage Backend",
"Current data downloaded.": "Current data downloaded.",
@ -625,6 +635,7 @@
"DELETE COMPLETE": "DELETE COMPLETE",
"DELETE FAILED": "DELETE FAILED",
"DELETE_IN PROGRESS": "DELETE_IN PROGRESS",
"DENY": "DENY",
"DHCP": "DHCP",
"DHCP Agent": "DHCP Agent",
"DHCP Agents": "DHCP Agents",
@ -699,6 +710,7 @@
"Delete Extra Specs": "Delete Extra Specs",
"Delete Failed": "Delete Failed",
"Delete File": "Delete File",
"Delete Firewall": "Delete Firewall",
"Delete Flavor": "Delete Flavor",
"Delete Folder": "Delete Folder",
"Delete Group": "Delete Group",
@ -715,6 +727,7 @@
"Delete Metadata": "Delete Metadata",
"Delete Network": "Delete Network",
"Delete Node": "Delete Node",
"Delete Policy": "Delete Policy",
"Delete Port": "Delete Port",
"Delete Port Forwarding": "Delete Port Forwarding",
"Delete Port Group": "Delete Port Group",
@ -766,6 +779,9 @@
"Dest Folder": "Dest Folder",
"Destination": "Destination",
"Destination CIDR": "Destination CIDR",
"Destination IP": "Destination IP",
"Destination IP Address/Subnet": "Destination IP Address/Subnet",
"Destination Port": "Destination Port",
"Destination Port/Port Range": "Destination Port/Port Range",
"Detach": "Detach",
"Detach Instance": "Detach Instance",
@ -780,7 +796,9 @@
"Details": "Details",
"Details *": "Details *",
"Details about the PTR record.": "Details about the PTR record.",
"Device": "Device",
"Device ID": "Device ID",
"Device ID/Name": "Device ID/Name",
"Device Owner": "Device Owner",
"Devicemapper": "Devicemapper",
"Direct": "Direct",
@ -894,10 +912,14 @@
"Edit host aggregate": "Edit host aggregate",
"Edit metadata": "Edit metadata",
"Edit quota": "Edit quota",
"Edit rule": "Edit rule",
"Editing only changes the content of the file, not the file name.": "Editing only changes the content of the file, not the file name.",
"Effective Mode": "Effective Mode",
"Effective mode after configuration changes": "Effective mode after configuration changes",
"Egress": "Egress",
"Egress Policy": "Egress Policy",
"Egress Policy ID": "Egress Policy ID",
"Egress Policy Name": "Egress Policy Name",
"Egypt": "Egypt",
"Eject": "Eject",
"El Salvador": "El Salvador",
@ -1010,6 +1032,14 @@
"Fingerprint": "Fingerprint",
"Finish Resize": "Finish Resize",
"Finland": "Finland",
"Firewall": "Firewall",
"Firewall Detail": "Firewall Detail",
"Firewall Policies": "Firewall Policies",
"Firewall Policy": "Firewall Policy",
"Firewall Port": "Firewall Port",
"Firewall Rule": "Firewall Rule",
"Firewall Rules": "Firewall Rules",
"Firewalls": "Firewalls",
"Fixed IP": "Fixed IP",
"Fixed IP Address": "Fixed IP Address",
"Fixed IPs": "Fixed IPs",
@ -1132,6 +1162,9 @@
"Heterogeneous Computing": "Heterogeneous Computing",
"Hidden": "Hidden",
"Hide Advanced Options": "Hide Advanced Options",
"Hide Default Firewalls": "Hide Default Firewalls",
"Hide Default Policies": "Hide Default Policies",
"Hide Default Rules": "Hide Default Rules",
"High Clock Speed": "High Clock Speed",
"Home": "Home",
"Home page": "Home page",
@ -1155,6 +1188,7 @@
"Hungary": "Hungary",
"Hypervisor Detail": "Hypervisor Detail",
"Hypervisors": "Hypervisors",
"ICMP": "ICMP",
"ICMP Code": "ICMP Code",
"ICMP Type": "ICMP Type",
"ICMP Type/ICMP Code": "ICMP Type/ICMP Code",
@ -1263,6 +1297,9 @@
"Infinity": "Infinity",
"Info": "Info",
"Ingress": "Ingress",
"Ingress Policy": "Ingress Policy",
"Ingress Policy ID": "Ingress Policy ID",
"Ingress Policy Name": "Ingress Policy Name",
"Init Complete": "Init Complete",
"Init Failed": "Init Failed",
"Init In Progress": "Init In Progress",
@ -1276,6 +1313,10 @@
"Input internal port or port range (example: 80 or 80:160)": "Input internal port or port range (example: 80 or 80:160)",
"Input source port or port range (example: 80 or 80:160)": "Input source port or port range (example: 80 or 80:160)",
"Insecure Registry": "Insecure Registry",
"Insert": "Insert",
"Insert After": "Insert After",
"Insert Before": "Insert Before",
"Insert Rule": "Insert Rule",
"Inspect Failed": "Inspect Failed",
"Inspecting": "Inspecting",
"Instance": "Instance",
@ -1306,6 +1347,7 @@
"Instance ID": "Instance ID",
"Instance IP": "Instance IP",
"Instance Info": "Instance Info",
"Instance Port": "Instance Port",
"Instance Related": "Instance Related",
"Instance Snapshot": "Instance Snapshot",
"Instance Snapshot Detail": "Instance Snapshot Detail",
@ -1492,6 +1534,7 @@
"Manage Error": "Manage Error",
"Manage Host": "Manage Host",
"Manage Metadata": "Manage Metadata",
"Manage Ports": "Manage Ports",
"Manage QoS Spec": "Manage QoS Spec",
"Manage Resource Types": "Manage Resource Types",
"Manage Security Group": "Manage Security Group",
@ -1617,6 +1660,7 @@
"Network Dropped Packets": "Network Dropped Packets",
"Network Errors": "Network Errors",
"Network ID": "Network ID",
"Network ID/Name": "Network ID/Name",
"Network Info": "Network Info",
"Network Interface": "Network Interface",
"Network Line": "Network Line",
@ -1903,7 +1947,10 @@
"Pointer Record": "Pointer Record",
"Poland": "Poland",
"Policy": "Policy",
"Policy Detail": "Policy Detail",
"Policy Edit": "Policy Edit",
"Policy Name": "Policy Name",
"Policy Rules": "Policy Rules",
"Pool Algorithm": "Pool Algorithm",
"Pool Description": "Pool Description",
"Pool Detail": "Pool Detail",
@ -2013,6 +2060,7 @@
"RAM": "RAM",
"RAM (MiB)": "RAM (MiB)",
"RAW - Raw disk image format": "RAW - Raw disk image format",
"REJECT": "REJECT",
"RESTORE COMPLETE": "RESTORE COMPLETE",
"RESUME COMPLETE": "RESUME COMPLETE",
"RESUME FAILED": "RESUME FAILED",
@ -2054,6 +2102,7 @@
"Recycle Bin": "Recycle Bin",
"Region": "Region",
"Registry Enabled": "Registry Enabled",
"Related Policy": "Related Policy",
"Related Resources": "Related Resources",
"Release": "Release",
"Release Fixed IP": "Release Fixed IP",
@ -2065,6 +2114,7 @@
"Remove Default Project": "Remove Default Project",
"Remove Network": "Remove Network",
"Remove Router": "Remove Router",
"Remove Rule": "Remove Rule",
"Remove default project for user": "Remove default project for user",
"Rename": "Rename",
"Rename is to copy the current file to the new file address and delete the current file, which will affect the creation time of the file.": "Rename is to copy the current file to the new file address and delete the current file, which will affect the creation time of the file.",
@ -2144,7 +2194,12 @@
"Router Detail": "Router Detail",
"Router External": "Router External",
"Router ID": "Router ID",
"Router Port": "Router Port",
"Routers": "Routers",
"Rule": "Rule",
"Rule Action": "Rule Action",
"Rule Detail": "Rule Detail",
"Rule Edit": "Rule Edit",
"Rule Numbers": "Rule Numbers",
"Rules": "Rules",
"Rules Number": "Rules Number",
@ -2266,6 +2321,7 @@
"Shared Network": "Shared Network",
"Shared Networks": "Shared Networks",
"Shared QoS Policies": "Shared QoS Policies",
"Shared policy only can insert shared rules.": "Shared policy only can insert shared rules.",
"Shares": "Shares",
"Shelve": "Shelve",
"Shelve Instance": "Shelve Instance",
@ -2317,7 +2373,10 @@
"Somalia": "Somalia",
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.",
"Source": "Source",
"Source IP": "Source IP",
"Source IP Address/Subnet": "Source IP Address/Subnet",
"Source Path: {path}": "Source Path: {path}",
"Source Port": "Source Port",
"Source Port/Port Range": "Source Port/Port Range",
"South Africa": "South Africa",
"South Korea": "South Korea",
@ -2810,7 +2869,11 @@
"Yemen": "Yemen",
"Yes": "Yes",
"Yes - Create a new system disk": "Yes - Create a new system disk",
"You are not allowed to delete policy \"{ name }\" used by firewalls: { firewalls }.": "You are not allowed to delete policy \"{ name }\" used by firewalls: { firewalls }.",
"You are not allowed to delete policy \"{ name }\".": "You are not allowed to delete policy \"{ name }\".",
"You are not allowed to delete router \"{ name }\".": "You are not allowed to delete router \"{ name }\".",
"You are not allowed to delete rule \"{ name }\" in use.": "You are not allowed to delete rule \"{ name }\" in use.",
"You are not allowed to delete rule \"{ name }\".": "You are not allowed to delete rule \"{ name }\".",
"You are not allowed to delete snapshot \"{ name }\", which is used by creating volume \"{volumes}\".": "You are not allowed to delete snapshot \"{ name }\", which is used by creating volume \"{volumes}\".",
"You are not allowed to delete snapshot \"{ name }\".": "You are not allowed to delete snapshot \"{ name }\".",
"You are not allowed to jump to the console.": "You are not allowed to jump to the console.",
@ -2874,6 +2937,7 @@
"create baremetal node": "create baremetal node",
"create default pool": "create default pool",
"create encryption": "create encryption",
"create firewall policy": "create firewall policy",
"create flavor": "create flavor",
"create instance snapshot": "create instance snapshot",
"create ipsec site connection": "create ipsec site connection",
@ -2905,6 +2969,7 @@
"delete default pool": "delete default pool",
"delete domain": "delete domain",
"delete dscp marking rules": "delete dscp marking rules",
"delete firewall": "delete firewall",
"delete flavor": "delete flavor",
"delete group": "delete group",
"delete host": "delete host",
@ -2918,11 +2983,13 @@
"delete load balancer": "delete load balancer",
"delete member": "delete member",
"delete network": "delete network",
"delete policy": "delete policy",
"delete port forwarding": "delete port forwarding",
"delete project": "delete project",
"delete qos policy": "delete qos policy",
"delete role": "delete role",
"delete router": "delete router",
"delete rule": "delete rule",
"delete segments": "delete segments",
"delete stack": "delete stack",
"delete static route": "delete static route",
@ -2962,6 +3029,11 @@
"external port": "external port",
"external ports": "external ports",
"extra specs": "extra specs",
"firewall": "firewall",
"firewall policies": "firewall policies",
"firewall rule": "firewall rule",
"firewall rules": "firewall rules",
"firewalls": "firewalls",
"flavor": "flavor",
"floating ip": "floating ip",
"floating ips": "floating ips",
@ -2974,6 +3046,7 @@
"in": "in",
"ingress": "ingress",
"insert": "insert",
"insert rule": "insert rule",
"instance": "instance",
"instance snapshot": "instance snapshot",
"instance snapshots": "instance snapshots",
@ -2992,6 +3065,7 @@
"live migrate": "live migrate",
"load balancer": "load balancer",
"lock instance": "lock instance",
"manage ports": "manage ports",
"manage qos spec": "manage qos spec",
"manage resource types": "manage resource types",
"message": "message",
@ -3012,6 +3086,7 @@
"phone": "phone",
"please select network": "please select network",
"please select subnet": "please select subnet",
"policy": "policy",
"port": "port",
"port forwarding": "port forwarding",
"port forwardings": "port forwardings",
@ -3033,6 +3108,7 @@
"release fixed ip": "release fixed ip",
"remove network": "remove network",
"remove router": "remove router",
"remove rule": "remove rule",
"reserved_host": "reserved_host",
"resize": "resize",
"resume instance": "resume instance",
@ -3116,6 +3192,8 @@
"vpn endpoint groups": "vpn endpoint groups",
"vpn services": "vpn services",
"write": "write",
"{ name } Format Error (e.g. 192.168.1.1 or 192.168.1.1/24)": "{ name } Format Error (e.g. 192.168.1.1 or 192.168.1.1/24)",
"{ name } Format Error (e.g. FE80:0:0:0:0:0:0:1 or FE80:0:0:0:0:0:0:1/10)": "{ name } Format Error (e.g. FE80:0:0:0:0:0:0:1 or FE80:0:0:0:0:0:0:1/10)",
"{ size } GiB": "{ size } GiB",
"{ size } KiB": "{ size } KiB",
"{ size } MiB": "{ size } MiB",

View File

@ -34,6 +34,7 @@
"A dynamic scheduling algorithm that estimates the server load based on the number of currently active connections. The system allocates new connection requests to the server with the least number of current connections. Commonly used for long connection services, such as database connections and other services.": "동적 스케쥴링 알고리즘은 현재 연결된 커넥션 수로 서버 로드를 예측합니다. 시스템은 최소 연결을 가진 서버로 새로운 연결을 할당합니다. 데이터베이스 연결 및 다른 서비스 처럼 오래 지속되는 연결을 갖는 서비스에 활용됩니다.",
"A host aggregate can be associated with at most one AZ. Once the association is established, the AZ cannot be disassociated.": "호스트 집합은 한개의 AZ와 결합될 수 있습니다. 결합되고 나면 AZ는 분리될 수 없습니다.",
"A public container will allow anyone to use the objects in your container through a public URL.": "퍼블릭 컨테이너는 퍼블릭 URL을 통해 컨테이너 내부의 객체를 사용할 수 있습니다.",
"A rule specified before insertion or after insertion a rule. If both are not specified, the new rule is inserted as the first rule of the policy.": "",
"A snapshot is an image which preserves the disk state of a running instance, which can be used to start a new instance.": "스냅샷은 실행중인 인스턴의 디스크 상태를 보존하거나 새로운 인스턴스를 시작할 수 있는 이미지입니다.",
"A template is a YAML file that contains configuration information, please enter the correct format.": "템플릿은 설정 정보를 포함한 YAML 파일입니다. 올바른 형식으로 입력해주세요.",
"A template is a YAML file that contains configuration information.": "템플릿은 설정 정보를 포한한 YAML 파일입니다.",
@ -41,6 +42,7 @@
"ADOPT COMPLETE": "적용 완료",
"AH": "AH",
"AKI - Amazon kernel image format": "AKI - 아마존 커널 이미지 형식",
"ALLOW": "",
"AMI - Amazon server image format": "AMI - 아마존 서버 이미지 형식",
"ANY": "ANY",
"API Address": "API 주소",
@ -78,6 +80,7 @@
"Add Metadata": "메타데이터 추가",
"Add NUMA Node": "NUMA 노드 추가",
"Add Network": "네트워크 추가",
"Add Policy": "",
"Add Property": "속성 추가",
"Add Router": "라우터 추가",
"Add Virtual LAN": "가상 LAN 추가",
@ -174,6 +177,7 @@
"Associate Floating IP": "유동 IP 연결",
"Associate IP": "IP 연결",
"Associate Network": "네트워크 연결",
"Associated Ports": "",
"Associated QoS Spec ID": "관련 QoS 사양 ID",
"Associated QoS Spec ID/Name": "관련 QoS 사양 ID/이름",
"Associated Resource": "연결된 리소스",
@ -193,6 +197,7 @@
"Attaching": "연결",
"Attachments Info": "첨부 정보",
"Attributes": "속성",
"Audited": "",
"Australia": "호주",
"Austria": "오스트리아",
"Auth Algorithm": "인증 알고리즘",
@ -513,6 +518,8 @@
"Create Encryption": "암호화 생성",
"Create Extra Spec": "추가 사양 생성",
"Create Failed": "생성 실패",
"Create Firewall": "",
"Create Firewall Policy": "",
"Create Flavor": "Flavor 생성",
"Create Folder": "폴더 생성",
"Create Host Aggregate": "호스트 집계 생성",
@ -528,6 +535,7 @@
"Create Network": "네트워크 생성",
"Create New Network": "새로운 네트워크 생성",
"Create Node": "노드 생성",
"Create Policy": "",
"Create Port": "포트 생성",
"Create Port Forwarding": "포트 포워딩 생성",
"Create Port Group": "포트 그룹 생성",
@ -567,6 +575,7 @@
"Create Volume Type": "볼륨 유형 생성 ",
"Create Zone": "Zone 생성",
"Create a full backup, the system will automatically create a new backup chain, the full backup name is the backup chain name; Create an incremental backup, the system will automatically create an incremental backup under the newly created backup chain.": "전체 백업을 생성하면, 시스템이 새 백업 체인을 자동으로 생성하고, 전체 백업 이름은 백업 체인 이름입니다. 증분 백업을 생성하면, 시스템이 새로 생성된 백업 체인 아래에 증분 백업을 자동으로 생성합니다.",
"Create firewall": "",
"Create host aggregate": "호스트 집계 생성",
"Create image": "이미지 생성",
"Create instance": "인스턴스 생성",
@ -604,6 +613,7 @@
"Current Project Networks": "현재 프로젝트 네트워크",
"Current Project QoS Policies": "현재 프로젝트 QoS 정책",
"Current QoS policy name": "현재 QoS 정책 이름",
"Current Rules": "",
"Current Status": "현재 상태",
"Current Storage Backend": "현재 스토리지 백엔드",
"Current data downloaded.": "현재 데이터가 다운로드되었습니다.",
@ -625,6 +635,7 @@
"DELETE COMPLETE": "삭제 성공",
"DELETE FAILED": "삭제 실패",
"DELETE_IN PROGRESS": "삭제 진행중",
"DENY": "",
"DHCP": "DHCP",
"DHCP Agent": "DHCP 에이전트",
"DHCP Agents": "DHCP 에이전트들",
@ -699,6 +710,7 @@
"Delete Extra Specs": "Extra Specs 삭제",
"Delete Failed": "삭제 실패",
"Delete File": "File 삭제",
"Delete Firewall": "",
"Delete Flavor": "Flavor 삭제",
"Delete Folder": "Folder 삭제",
"Delete Group": "Group 삭제",
@ -715,6 +727,7 @@
"Delete Metadata": "Metadata 삭제",
"Delete Network": "Network 삭제",
"Delete Node": "Node 삭제",
"Delete Policy": "",
"Delete Port": "Port 삭제",
"Delete Port Forwarding": "Port Forwarding 삭제",
"Delete Port Group": "Port Group 삭제",
@ -766,6 +779,9 @@
"Dest Folder": "",
"Destination": "목적지",
"Destination CIDR": "",
"Destination IP": "",
"Destination IP Address/Subnet": "",
"Destination Port": "",
"Destination Port/Port Range": "",
"Detach": "",
"Detach Instance": "",
@ -780,7 +796,9 @@
"Details": "",
"Details *": "",
"Details about the PTR record.": "PTR record의 상세정보.",
"Device": "",
"Device ID": "장치 ID",
"Device ID/Name": "",
"Device Owner": "",
"Devicemapper": "",
"Direct": "",
@ -894,10 +912,14 @@
"Edit host aggregate": "Host Aggregate 편집",
"Edit metadata": "Metadata 편집",
"Edit quota": "Quota 편집",
"Edit rule": "",
"Editing only changes the content of the file, not the file name.": "편집은 파일 이름이 아닌 파일의 내용만 변경합니다.",
"Effective Mode": "",
"Effective mode after configuration changes": "설정 변경 후 Effective Mode",
"Egress": "",
"Egress Policy": "",
"Egress Policy ID": "",
"Egress Policy Name": "",
"Egypt": "",
"Eject": "",
"El Salvador": "",
@ -1010,6 +1032,14 @@
"Fingerprint": "",
"Finish Resize": "Resize 완료",
"Finland": "",
"Firewall": "",
"Firewall Detail": "",
"Firewall Policies": "",
"Firewall Policy": "",
"Firewall Port": "",
"Firewall Rule": "",
"Firewall Rules": "",
"Firewalls": "",
"Fixed IP": "고정 IP",
"Fixed IP Address": "고정 IP 주소",
"Fixed IPs": "고정 IP",
@ -1132,6 +1162,9 @@
"Heterogeneous Computing": "",
"Hidden": "숨겨짐",
"Hide Advanced Options": "고급 옵션 숨기기",
"Hide Default Firewalls": "",
"Hide Default Policies": "",
"Hide Default Rules": "",
"High Clock Speed": "",
"Home": "홈페이지",
"Home page": "홈페이지",
@ -1155,6 +1188,7 @@
"Hungary": "",
"Hypervisor Detail": "Hypervisor 상세 정보",
"Hypervisors": "",
"ICMP": "",
"ICMP Code": "ICMP 코드",
"ICMP Type": "ICMP 타입",
"ICMP Type/ICMP Code": "ICMP 타입/ICMP 코드",
@ -1263,6 +1297,9 @@
"Infinity": "무한대",
"Info": "정보",
"Ingress": "인그레스",
"Ingress Policy": "",
"Ingress Policy ID": "",
"Ingress Policy Name": "",
"Init Complete": "초기화 완료",
"Init Failed": "초기화 실패",
"Init In Progress": "초기화 진행 중",
@ -1276,6 +1313,10 @@
"Input internal port or port range (example: 80 or 80:160)": "입력 내부 포트 또는 포트 범위(예: 80 또는 80:160)",
"Input source port or port range (example: 80 or 80:160)": "입력 소스 포트 또는 포트 범위(예: 80 또는 80:160)",
"Insecure Registry": "암호화되지 않은 레지스트리",
"Insert": "",
"Insert After": "",
"Insert Before": "",
"Insert Rule": "",
"Inspect Failed": "검사 실패",
"Inspecting": "검사 중",
"Instance": "인스턴스",
@ -1306,6 +1347,7 @@
"Instance ID": "인스턴스 ID",
"Instance IP": "인스턴스 IP",
"Instance Info": "인스턴스 정보",
"Instance Port": "",
"Instance Related": "인스턴스 관련",
"Instance Snapshot": "인스턴스 스냅샷",
"Instance Snapshot Detail": "인스턴스 스냅샷 세부 정보",
@ -1492,6 +1534,7 @@
"Manage Error": "오류 관리",
"Manage Host": "호스트 관리",
"Manage Metadata": "메타데이터 관리",
"Manage Ports": "",
"Manage QoS Spec": "QOS 스펙 관리",
"Manage Resource Types": "리소스 타입 관리",
"Manage Security Group": "보안 그룹 관리",
@ -1617,6 +1660,7 @@
"Network Dropped Packets": "",
"Network Errors": "",
"Network ID": "네트워크 ID",
"Network ID/Name": "",
"Network Info": "네트워크 정보",
"Network Interface": "네트워크 인터페이스",
"Network Line": "",
@ -1903,7 +1947,10 @@
"Pointer Record": "포인터 레코드",
"Poland": "",
"Policy": "정책",
"Policy Detail": "",
"Policy Edit": "",
"Policy Name": "정책 이름",
"Policy Rules": "",
"Pool Algorithm": "풀 알고리즘",
"Pool Description": "풀 설명",
"Pool Detail": "풀 상세 정보",
@ -2013,6 +2060,7 @@
"RAM": "",
"RAM (MiB)": "",
"RAW - Raw disk image format": "RAW - 원본 디스크 이미지 형식",
"REJECT": "",
"RESTORE COMPLETE": "복원 완료",
"RESUME COMPLETE": "재개 완료",
"RESUME FAILED": "재개 실패",
@ -2054,6 +2102,7 @@
"Recycle Bin": "휴지통",
"Region": "지역",
"Registry Enabled": "레지스트리 활성화됨",
"Related Policy": "",
"Related Resources": "관련 리소스",
"Release": "릴리스",
"Release Fixed IP": "고정 IP 해제",
@ -2065,6 +2114,7 @@
"Remove Default Project": "기본 프로젝트 제거",
"Remove Network": "네트워크 제거",
"Remove Router": "라우터 제거",
"Remove Rule": "",
"Remove default project for user": "사용자의 기본 프로젝트 제거",
"Rename": "이름 변경",
"Rename is to copy the current file to the new file address and delete the current file, which will affect the creation time of the file.": "이름 변경은 현재 파일을 새 파일 주소로 복사한 후 현재 파일을 삭제하는 것을 의미하며, 파일의 생성 시간에 영향을 줍니다.",
@ -2144,7 +2194,12 @@
"Router Detail": "라우터 상세 정보",
"Router External": "라우터 외부",
"Router ID": "라우터 ID",
"Router Port": "",
"Routers": "라우터",
"Rule": "",
"Rule Action": "",
"Rule Detail": "",
"Rule Edit": "",
"Rule Numbers": "규칙 수",
"Rules": "규칙",
"Rules Number": "규칙 수",
@ -2266,6 +2321,7 @@
"Shared Network": "공유 네트워크",
"Shared Networks": "공유 네트워크",
"Shared QoS Policies": "공유 QoS 정책",
"Shared policy only can insert shared rules.": "",
"Shares": "",
"Shelve": "",
"Shelve Instance": "",
@ -2317,7 +2373,10 @@
"Somalia": "",
"Sorry, the page you visited does not exist.": "",
"Source": "",
"Source IP": "",
"Source IP Address/Subnet": "",
"Source Path: {path}": "",
"Source Port": "",
"Source Port/Port Range": "",
"South Africa": "",
"South Korea": "",
@ -2810,7 +2869,11 @@
"Yemen": "",
"Yes": "예",
"Yes - Create a new system disk": "예 - 새 시스템 디스크를 만듭니다",
"You are not allowed to delete policy \"{ name }\" used by firewalls: { firewalls }.": "",
"You are not allowed to delete policy \"{ name }\".": "",
"You are not allowed to delete router \"{ name }\".": "",
"You are not allowed to delete rule \"{ name }\" in use.": "",
"You are not allowed to delete rule \"{ name }\".": "",
"You are not allowed to delete snapshot \"{ name }\", which is used by creating volume \"{volumes}\".": "",
"You are not allowed to delete snapshot \"{ name }\".": "",
"You are not allowed to jump to the console.": "",
@ -2874,6 +2937,7 @@
"create baremetal node": "",
"create default pool": "",
"create encryption": "암호화 생성",
"create firewall policy": "",
"create flavor": "flavor 생성",
"create instance snapshot": "인스턴스 스냅샷 생성",
"create ipsec site connection": "",
@ -2905,6 +2969,7 @@
"delete default pool": "",
"delete domain": "도메인 삭제",
"delete dscp marking rules": "",
"delete firewall": "",
"delete flavor": "flavor 삭제",
"delete group": "그룹 삭제",
"delete host": "",
@ -2918,11 +2983,13 @@
"delete load balancer": "로드 밸런서 삭제",
"delete member": "멤버 삭제",
"delete network": "네트워크 삭제",
"delete policy": "",
"delete port forwarding": "",
"delete project": "프로젝트 삭제",
"delete qos policy": "QoS 정책 삭제",
"delete role": "역할 삭제",
"delete router": "라우터 삭제",
"delete rule": "",
"delete segments": "",
"delete stack": "",
"delete static route": "정적 경로 삭제",
@ -2962,6 +3029,11 @@
"external port": "외부 포트",
"external ports": "외부 포트",
"extra specs": "추가 사양",
"firewall": "",
"firewall policies": "",
"firewall rule": "",
"firewall rules": "",
"firewalls": "",
"flavor": "flavor",
"floating ip": "유동 ip",
"floating ips": "유동 ip",
@ -2974,6 +3046,7 @@
"in": "",
"ingress": "",
"insert": "",
"insert rule": "",
"instance": "",
"instance snapshot": "인스턴스 스냅샷",
"instance snapshots": "인스턴스 스냅샷",
@ -2992,6 +3065,7 @@
"live migrate": "",
"load balancer": "로드 밸런서",
"lock instance": "",
"manage ports": "",
"manage qos spec": "QOS 스펙 관리",
"manage resource types": "리소스 타입 관리",
"message": "메시지",
@ -3012,6 +3086,7 @@
"phone": "",
"please select network": "네트워크 선택",
"please select subnet": "서브넷 선택",
"policy": "",
"port": "포트",
"port forwarding": "",
"port forwardings": "",
@ -3033,6 +3108,7 @@
"release fixed ip": "",
"remove network": "네트워크 삭제",
"remove router": "라우터 삭제",
"remove rule": "",
"reserved_host": "",
"resize": "크기 변경",
"resume instance": "인스턴스 재시작",
@ -3116,6 +3192,8 @@
"vpn endpoint groups": "",
"vpn services": "",
"write": "",
"{ name } Format Error (e.g. 192.168.1.1 or 192.168.1.1/24)": "",
"{ name } Format Error (e.g. FE80:0:0:0:0:0:0:1 or FE80:0:0:0:0:0:0:1/10)": "",
"{ size } GiB": "",
"{ size } KiB": "",
"{ size } MiB": "",

View File

@ -34,6 +34,7 @@
"A dynamic scheduling algorithm that estimates the server load based on the number of currently active connections. The system allocates new connection requests to the server with the least number of current connections. Commonly used for long connection services, such as database connections and other services.": "Динамический алгоритм планирования, который оценивает нагрузку сервера на основе количества активных соединений. Система выделяет новые запросы на соединение серверу с наименьшим количеством текущих соединений. Часто используется для долгосрочных сервисов с соединением, таких как подключения к базам данных и другие сервисы.",
"A host aggregate can be associated with at most one AZ. Once the association is established, the AZ cannot be disassociated.": "Хост-агрегат может быть связан максимум с одной зоной доступности. После установления связи, её нельзя разорвать.",
"A public container will allow anyone to use the objects in your container through a public URL.": "Публичный контейнер позволит любому пользователю использовать объекты в вашем контейнере по общедоступному URL-адресу.",
"A rule specified before insertion or after insertion a rule. If both are not specified, the new rule is inserted as the first rule of the policy.": "",
"A snapshot is an image which preserves the disk state of a running instance, which can be used to start a new instance.": "Снимок - это образ, который сохраняет состояние диска работающего инстанса и может быть использовано для запуска нового инстанса.",
"A template is a YAML file that contains configuration information, please enter the correct format.": "Шаблон - это YAML-файл, который содержит информацию о конфигурации. Пожалуйста, введите правильный формат.",
"A template is a YAML file that contains configuration information.": "Шаблон - это YAML-файл, который содержит информацию о конфигурации.",
@ -41,6 +42,7 @@
"ADOPT COMPLETE": "ПРИНЯТО ЗАВЕРШЕНО",
"AH": "AH",
"AKI - Amazon kernel image format": "AKI - формат образа ядра Amazon",
"ALLOW": "",
"AMI - Amazon server image format": "AMI - формат образа сервера Amazon",
"ANY": "ЛЮБОЙ",
"API Address": "Адрес API",
@ -78,6 +80,7 @@
"Add Metadata": "Добавить метаданные",
"Add NUMA Node": "Добавить узел NUMA",
"Add Network": "Добавить сеть",
"Add Policy": "",
"Add Property": "Добавить свойство",
"Add Router": "Добавить маршрутизатор",
"Add Virtual LAN": "Добавить виртуальную LAN",
@ -174,6 +177,7 @@
"Associate Floating IP": "Ассоциировать плавающий IP",
"Associate IP": "Ассоциировать IP",
"Associate Network": "Ассоциировать сеть",
"Associated Ports": "",
"Associated QoS Spec ID": "Идентификатор связанной спецификации QoS",
"Associated QoS Spec ID/Name": "Идентификатор/имя связанной спецификации QoS",
"Associated Resource": "Связанный ресурс",
@ -193,6 +197,7 @@
"Attaching": "Прикрепление",
"Attachments Info": "Информация о прикреплениях",
"Attributes": "Атрибуты",
"Audited": "",
"Australia": "Австралия",
"Austria": "Австрия",
"Auth Algorithm": "Алгоритм аутентификации",
@ -513,6 +518,8 @@
"Create Encryption": "",
"Create Extra Spec": "",
"Create Failed": "",
"Create Firewall": "",
"Create Firewall Policy": "",
"Create Flavor": "",
"Create Folder": "",
"Create Host Aggregate": "",
@ -528,6 +535,7 @@
"Create Network": "Создать сеть",
"Create New Network": "Создать новую сеть",
"Create Node": "Создать узел",
"Create Policy": "",
"Create Port": "Создать порт",
"Create Port Forwarding": "Создать переадресацию портов",
"Create Port Group": "Создать группу портов",
@ -567,6 +575,7 @@
"Create Volume Type": "Создать тип диск",
"Create Zone": "Создать зону",
"Create a full backup, the system will automatically create a new backup chain, the full backup name is the backup chain name; Create an incremental backup, the system will automatically create an incremental backup under the newly created backup chain.": "Создать полную резервную копию, система автоматически создаст новую цепочку резервных копий, имя полной резервной копии будет именем цепочки резервных копий; Создать инкрементную резервную копию, система автоматически создаст инкрементную резервную копию вновь созданной цепочки резервных копий.",
"Create firewall": "",
"Create host aggregate": "Создать хост-агрегат",
"Create image": "Создать образ",
"Create instance": "Создать инстанс",
@ -604,6 +613,7 @@
"Current Project Networks": "Сети текущего проекта",
"Current Project QoS Policies": "Политики QoS текущего проекта",
"Current QoS policy name": "Имя текущей политики QoS",
"Current Rules": "",
"Current Status": "Текущий статус",
"Current Storage Backend": "Текущий бэкенд хранилища",
"Current data downloaded.": "Текущие загруженные данные.",
@ -625,6 +635,7 @@
"DELETE COMPLETE": "УДАЛЕНИЕ ЗАВЕРШЕНО",
"DELETE FAILED": "УДАЛЕНИЕ НЕ УДАЛОСЬ",
"DELETE_IN PROGRESS": "УДАЛЕНИЕ В ПРОЦЕССЕ",
"DENY": "",
"DHCP": "DHCP",
"DHCP Agent": "Агент DHCP",
"DHCP Agents": "Агенты DHCP",
@ -699,6 +710,7 @@
"Delete Extra Specs": "Удалить дополнительные характеристики",
"Delete Failed": "Удаление не удалось",
"Delete File": "Удалить файл",
"Delete Firewall": "",
"Delete Flavor": "Удалить тип",
"Delete Folder": "Удалить папку",
"Delete Group": "Удалить группу",
@ -715,6 +727,7 @@
"Delete Metadata": "Удалить метаданные",
"Delete Network": "Удалить сеть",
"Delete Node": "Удалить узел",
"Delete Policy": "",
"Delete Port": "Удалить порт",
"Delete Port Forwarding": "Удалить перенаправление порта",
"Delete Port Group": "Удалить группу портов",
@ -766,6 +779,9 @@
"Dest Folder": "Папка назначения",
"Destination": "Место назначения",
"Destination CIDR": "CIDR-адрес места назначения",
"Destination IP": "",
"Destination IP Address/Subnet": "",
"Destination Port": "",
"Destination Port/Port Range": "Порт/диапазон портов места назначения",
"Detach": "Отсоединить",
"Detach Instance": "Отсоединить инстанс",
@ -780,7 +796,9 @@
"Details": "Подробности",
"Details *": "Подробности *",
"Details about the PTR record.": "Подробности о записи PTR.",
"Device": "",
"Device ID": "Идентификатор устройства",
"Device ID/Name": "",
"Device Owner": "Владелец устройства",
"Devicemapper": "Devicemapper",
"Direct": "Прямой",
@ -894,10 +912,14 @@
"Edit host aggregate": "Изменить группу хостов",
"Edit metadata": "Изменить метаданные",
"Edit quota": "Изменить квоты",
"Edit rule": "",
"Editing only changes the content of the file, not the file name.": "Редактирование изменяет только содержимое файла, не его имя.",
"Effective Mode": "Эффективный режим",
"Effective mode after configuration changes": "Эффективный режим после изменения типа инстанса",
"Egress": "Исходящий",
"Egress Policy": "",
"Egress Policy ID": "",
"Egress Policy Name": "",
"Egypt": "Египет",
"Eject": "Извлечь",
"El Salvador": "Сальвадор",
@ -1010,6 +1032,14 @@
"Fingerprint": "Отпечаток",
"Finish Resize": "Завершить изменение размера",
"Finland": "Финляндия",
"Firewall": "",
"Firewall Detail": "",
"Firewall Policies": "",
"Firewall Policy": "",
"Firewall Port": "",
"Firewall Rule": "",
"Firewall Rules": "",
"Firewalls": "",
"Fixed IP": "Фиксированный IP",
"Fixed IP Address": "Фиксированный IP-адрес",
"Fixed IPs": "Фиксированные IP-адреса",
@ -1132,6 +1162,9 @@
"Heterogeneous Computing": "Гетерогенные вычисления",
"Hidden": "Скрыто",
"Hide Advanced Options": "Скрыть расширенные опции",
"Hide Default Firewalls": "",
"Hide Default Policies": "",
"Hide Default Rules": "",
"High Clock Speed": "Высокая тактовая частота",
"Home": "Главная",
"Home page": "Главная страница",
@ -1155,6 +1188,7 @@
"Hungary": "Венгрия",
"Hypervisor Detail": "Подробности о гипервизоре",
"Hypervisors": "Гипервизоры",
"ICMP": "",
"ICMP Code": "Код ICMP",
"ICMP Type": "Тип ICMP",
"ICMP Type/ICMP Code": "Тип ICMP/Код ICMP",
@ -1263,6 +1297,9 @@
"Infinity": "Бесконечность",
"Info": "Информация",
"Ingress": "Входящий",
"Ingress Policy": "",
"Ingress Policy ID": "",
"Ingress Policy Name": "",
"Init Complete": "Завершено инициализация",
"Init Failed": "Инициализация не удалась",
"Init In Progress": "Идет инициализация",
@ -1276,6 +1313,10 @@
"Input internal port or port range (example: 80 or 80:160)": "Введите внутренний порт или диапазон портов (пример: 80 или 80:160)",
"Input source port or port range (example: 80 or 80:160)": "Введите исходный порт или диапазон портов (пример: 80 или 80:160)",
"Insecure Registry": "Ненадежный реестр",
"Insert": "",
"Insert After": "",
"Insert Before": "",
"Insert Rule": "",
"Inspect Failed": "Проверка не удалась",
"Inspecting": "Проверка",
"Instance": "Инстанс",
@ -1306,6 +1347,7 @@
"Instance ID": "Идентификатор инстанса",
"Instance IP": "IP-адрес инстанса",
"Instance Info": "Информация об инстансе",
"Instance Port": "",
"Instance Related": "Связанный инстанс",
"Instance Snapshot": "Снимок инстанса",
"Instance Snapshot Detail": "Подробности снимка инстанса",
@ -1492,6 +1534,7 @@
"Manage Error": "Управление ошибкой",
"Manage Host": "Управление хостом",
"Manage Metadata": "Управление метаданными",
"Manage Ports": "",
"Manage QoS Spec": "Управление спецификацией QoS",
"Manage Resource Types": "Управление типами ресурсов",
"Manage Security Group": "Управление группой безопасности",
@ -1617,6 +1660,7 @@
"Network Dropped Packets": "Сброшенные пакеты сети",
"Network Errors": "Ошибки сети",
"Network ID": "Идентификатор сети",
"Network ID/Name": "",
"Network Info": "Информация о сети",
"Network Interface": "Сетевой интерфейс",
"Network Line": "Линия сети",
@ -1903,7 +1947,10 @@
"Pointer Record": "Запись указателя",
"Poland": "Польша",
"Policy": "Политика",
"Policy Detail": "",
"Policy Edit": "",
"Policy Name": "Имя политики",
"Policy Rules": "",
"Pool Algorithm": "Алгоритм пула",
"Pool Description": "Описание пула",
"Pool Detail": "Подробности о пуле",
@ -2013,6 +2060,7 @@
"RAM": "ОЗУ",
"RAM (MiB)": "ОЗУ (МиБ)",
"RAW - Raw disk image format": "RAW",
"REJECT": "",
"RESTORE COMPLETE": "ВОССТАНОВЛЕНИЕ ЗАВЕРШЕНО",
"RESUME COMPLETE": "ВОЗОБНОВЛЕНИЕ ЗАВЕРШЕНО",
"RESUME FAILED": "ВОЗОБНОВЛЕНИЕ НЕ УДАЛОСЬ",
@ -2054,6 +2102,7 @@
"Recycle Bin": "Корзина",
"Region": "Регион",
"Registry Enabled": "Реестр включен",
"Related Policy": "",
"Related Resources": "Связанные ресурсы",
"Release": "Освободить",
"Release Fixed IP": "Освободить фиксированный IP",
@ -2065,6 +2114,7 @@
"Remove Default Project": "",
"Remove Network": "Удалить сеть",
"Remove Router": "Удалить маршрутизатор",
"Remove Rule": "",
"Remove default project for user": "",
"Rename": "Переименовать",
"Rename is to copy the current file to the new file address and delete the current file, which will affect the creation time of the file.": "Переименование - это копирование текущего файла по новому адресу файла и удаление текущего файла, что повлияет на время создания файла.",
@ -2144,7 +2194,12 @@
"Router Detail": "Подробности маршрутизатора",
"Router External": "Внешний маршрутизатор",
"Router ID": "Идентификатор маршрутизатора",
"Router Port": "",
"Routers": "Маршрутизаторы",
"Rule": "",
"Rule Action": "",
"Rule Detail": "",
"Rule Edit": "",
"Rule Numbers": "Номера правил",
"Rules": "Правила",
"Rules Number": "Количество правил",
@ -2266,6 +2321,7 @@
"Shared Network": "Общая сеть",
"Shared Networks": "Общие сети",
"Shared QoS Policies": "Общие политики QoS",
"Shared policy only can insert shared rules.": "",
"Shares": "Общий доступ",
"Shelve": "Архивировать",
"Shelve Instance": "Архивировать инстанс",
@ -2317,7 +2373,10 @@
"Somalia": "Сомали",
"Sorry, the page you visited does not exist.": "Извините, посещенная вами страница не существует",
"Source": "Источник",
"Source IP": "",
"Source IP Address/Subnet": "",
"Source Path: {path}": "Исходный путь: {path}",
"Source Port": "",
"Source Port/Port Range": "Исходный порт/диапазон портов",
"South Africa": "Южная Африка",
"South Korea": "Южная Корея",
@ -2810,7 +2869,11 @@
"Yemen": "Йемен",
"Yes": "Да",
"Yes - Create a new system disk": "",
"You are not allowed to delete policy \"{ name }\" used by firewalls: { firewalls }.": "",
"You are not allowed to delete policy \"{ name }\".": "",
"You are not allowed to delete router \"{ name }\".": "Вам запрещено удалять маршрутизатор \"{ name }\".",
"You are not allowed to delete rule \"{ name }\" in use.": "",
"You are not allowed to delete rule \"{ name }\".": "",
"You are not allowed to delete snapshot \"{ name }\", which is used by creating volume \"{volumes}\".": "Вам запрещено удалять снимок \"{ name }\", который используется для создания диска \"{volumes}\".",
"You are not allowed to delete snapshot \"{ name }\".": "Вам запрещено удалять снимок \"{ name }\".",
"You are not allowed to jump to the console.": "Вам запрещено перейти в консоль.",
@ -2874,6 +2937,7 @@
"create baremetal node": "создать физический узел",
"create default pool": "создать пул по умолчанию",
"create encryption": "создать шифрование",
"create firewall policy": "",
"create flavor": "создать конфигурацию",
"create instance snapshot": "создать снимок инстанса",
"create ipsec site connection": "создать IPsec-соединение между сайтами",
@ -2905,6 +2969,7 @@
"delete default pool": "удалить пул по умолчанию",
"delete domain": "удалить домен",
"delete dscp marking rules": "удалить правила маркировки DSCP",
"delete firewall": "",
"delete flavor": "удалить конфигурацию",
"delete group": "удалить группу",
"delete host": "",
@ -2918,11 +2983,13 @@
"delete load balancer": "удалить балансировщик нагрузки",
"delete member": "удалить участника",
"delete network": "удалить сеть",
"delete policy": "",
"delete port forwarding": "удалитылку портов",
"delete project": "удалить проект",
"delete qos policy": "удалить политику QoS",
"delete role": "удалить роль",
"delete router": "удалить маршрутизатор",
"delete rule": "",
"delete segments": "",
"delete stack": "удалить стек",
"delete static route": "удалить статический маршрут",
@ -2962,6 +3029,11 @@
"external port": "внешний порт",
"external ports": "внешние порты",
"extra specs": "дополнительные характеристики",
"firewall": "",
"firewall policies": "",
"firewall rule": "",
"firewall rules": "",
"firewalls": "",
"flavor": "конфигурация",
"floating ip": "плавающий IP-адрес",
"floating ips": "плавающие IP-адреса",
@ -2974,6 +3046,7 @@
"in": "в",
"ingress": "входящий",
"insert": "вставить",
"insert rule": "",
"instance": "инстанс",
"instance snapshot": "снимок инстанса",
"instance snapshots": "снимки инстанса",
@ -2992,6 +3065,7 @@
"live migrate": "живая миграция",
"load balancer": "балансировщик нагрузки",
"lock instance": "заблокировать инстанс",
"manage ports": "",
"manage qos spec": "управление спецификацией qos",
"manage resource types": "управление типами ресурсов",
"message": "сообщение",
@ -3012,6 +3086,7 @@
"phone": "телефон",
"please select network": "пожалуйста, выберите сеть",
"please select subnet": "пожалуйста, выберите подсеть",
"policy": "",
"port": "порт",
"port forwarding": "перенаправление порта",
"port forwardings": "перенаправления порта",
@ -3033,6 +3108,7 @@
"release fixed ip": "освободить фиксированный IP-адрес",
"remove network": "удалить сеть",
"remove router": "удалить маршрутизатор",
"remove rule": "",
"reserved_host": "",
"resize": "изменить размер",
"resume instance": "возобновить инстанс",
@ -3116,6 +3192,8 @@
"vpn endpoint groups": "группы конечных точек VPN",
"vpn services": "VPN-сервисы",
"write": "запись",
"{ name } Format Error (e.g. 192.168.1.1 or 192.168.1.1/24)": "",
"{ name } Format Error (e.g. FE80:0:0:0:0:0:0:1 or FE80:0:0:0:0:0:0:1/10)": "",
"{ size } GiB": "{ size } ГиБ",
"{ size } KiB": "{ size } КиБ",
"{ size } MiB": "{ size } МиБ",

View File

@ -34,6 +34,7 @@
"A dynamic scheduling algorithm that estimates the server load based on the number of currently active connections. The system allocates new connection requests to the server with the least number of current connections. Commonly used for long connection services, such as database connections and other services.": "Dinamik zamanlama algoritması, sunucu yükünü o anda açık olan bağlantı sayısından tahmin eder. Sistem, o anda en az sayıda bağlantıya sahip sunucuya yeni bağlantılar tahsis eder. Veritabanı bağlantıları gibi uzun ömürlü bağlantılara sahip hizmetler için kullanılır.",
"A host aggregate can be associated with at most one AZ. Once the association is established, the AZ cannot be disassociated.": "Bir ana bilgisayar kümesi, en fazla bir AZ ile ilişkilendirilebilir. İlişki kurulduktan sonra, AZ'nin ilişkisi kesilemez.",
"A public container will allow anyone to use the objects in your container through a public URL.": "Bir genel konteyner, herkesin konteyner içindeki nesneleri genel bir URL aracılığıyla kullanılabilir hale getirir.",
"A rule specified before insertion or after insertion a rule. If both are not specified, the new rule is inserted as the first rule of the policy.": "",
"A snapshot is an image which preserves the disk state of a running instance, which can be used to start a new instance.": "Anlık görüntü, çalışan bir sanal makinenin disk durumunu koruyan veya yeni bir sanal makinenin başlatılmasına izin veren bir imajdır.",
"A template is a YAML file that contains configuration information, please enter the correct format.": "Taslak, yapılandırma bilgilerini içeren bir YAML dosyasıdır. Lütfen doğru biçimde girin.",
"A template is a YAML file that contains configuration information.": "Taslak, yapılandırma bilgilerini içeren bir YAML dosyasıdır.",
@ -41,6 +42,7 @@
"ADOPT COMPLETE": "ALINMA TAMAMLANDI",
"AH": "AH",
"AKI - Amazon kernel image format": "AKI - Amazon çekirdek imaj biçimi",
"ALLOW": "",
"AMI - Amazon server image format": "AMI - Amazon sunucu imaj biçimi",
"ANY": "HERHANGİ BİR",
"API Address": "API Adresi",
@ -78,6 +80,7 @@
"Add Metadata": "Meta Veri Ekle",
"Add NUMA Node": "NUMA Düğümü Ekle",
"Add Network": "Ağ Ekle",
"Add Policy": "",
"Add Property": "Özellik Ekle",
"Add Router": "Yönlendirici Ekle",
"Add Virtual LAN": "Sanal LAN Ekle",
@ -174,6 +177,7 @@
"Associate Floating IP": "Değişken IP İlişkilendir",
"Associate IP": "IP İlişkilendir",
"Associate Network": "Ağ İlişkilendir",
"Associated Ports": "",
"Associated QoS Spec ID": "İlişkili QoS Spesifikasyon Kimliği",
"Associated QoS Spec ID/Name": "İlişkili QoS Spesifikasyon Kimliği/Adı",
"Associated Resource": "İlişkilendirilmiş Kaynak",
@ -193,6 +197,7 @@
"Attaching": "Ekleniyor",
"Attachments Info": "Eklentiler Bilgisi",
"Attributes": "Özellikler",
"Audited": "",
"Australia": "Avustralya",
"Austria": "Avusturya",
"Auth Algorithm": "Kimlik Doğrulama Algoritması",
@ -513,6 +518,8 @@
"Create Encryption": "Şifreleme Oluştur",
"Create Extra Spec": "Ek Özellik Oluştur",
"Create Failed": "Oluşturma Başarısız",
"Create Firewall": "",
"Create Firewall Policy": "",
"Create Flavor": "Şablon Oluştur",
"Create Folder": "Klasör Oluştur",
"Create Host Aggregate": "Ana Bilgisayar Kümesi Oluştur",
@ -528,6 +535,7 @@
"Create Network": "Ağ Oluştur",
"Create New Network": "Yeni Ağ Oluştur",
"Create Node": "Düğüm Oluştur",
"Create Policy": "",
"Create Port": "Ağ Adaptörü Oluştur",
"Create Port Forwarding": "Ağ Adaptörü Yönlendirme Oluştur",
"Create Port Group": "Ağ Adaptörü Grubu Oluştur",
@ -567,6 +575,7 @@
"Create Volume Type": "Disk Türü Oluştur",
"Create Zone": "Bölge Oluştur",
"Create a full backup, the system will automatically create a new backup chain, the full backup name is the backup chain name; Create an incremental backup, the system will automatically create an incremental backup under the newly created backup chain.": "Tam bir yedekleme oluşturun, sistem otomatik olarak yeni bir yedekleme zinciri oluşturacaktır, tam yedeklemenin adı yedekleme zinciri adıdır; Artan bir yedekleme oluşturun, sistem otomatik olarak yeni oluşturulan yedekleme zinciri altında artan bir yedekleme oluşturacaktır.",
"Create firewall": "",
"Create host aggregate": "Ana bilgisayar kümesi oluştur",
"Create image": "İmaj oluştur",
"Create instance": "Sanal Makine oluştur",
@ -604,6 +613,7 @@
"Current Project Networks": "Mevcut Proje Ağları",
"Current Project QoS Policies": "Mevcut Proje QoS İlkeleri",
"Current QoS policy name": "Mevcut QoS ilkesi adı",
"Current Rules": "",
"Current Status": "Mevcut Durum",
"Current Storage Backend": "Mevcut Depolama Arkayüzü",
"Current data downloaded.": "Mevcut veriler indirildi.",
@ -625,6 +635,7 @@
"DELETE COMPLETE": "SİLME TAMAMLANDI",
"DELETE FAILED": "SİLME BAŞARISIZ",
"DELETE_IN PROGRESS": "SİLME DEVAM EDİYOR",
"DENY": "",
"DHCP": "DHCP",
"DHCP Agent": "DHCP Ajanı",
"DHCP Agents": "DHCP Ajanları",
@ -699,6 +710,7 @@
"Delete Extra Specs": "Ek Özellikleri Sil",
"Delete Failed": "Silme Başarısız",
"Delete File": "Dosyayı Sil",
"Delete Firewall": "",
"Delete Flavor": "Şablonu Sil",
"Delete Folder": "Klasörü Sil",
"Delete Group": "Grubu Sil",
@ -715,6 +727,7 @@
"Delete Metadata": "Meta Veriyi Sil",
"Delete Network": "Ağı Sil",
"Delete Node": "Düğümü Sil",
"Delete Policy": "",
"Delete Port": "Ağ Adaptörünü Sil",
"Delete Port Forwarding": "Ağ Adaptörünü Yönlendirmesini Sil",
"Delete Port Group": "Ağ Adaptörünü Grubunu Sil",
@ -766,6 +779,9 @@
"Dest Folder": "Hedef Dosyası",
"Destination": "Hedef",
"Destination CIDR": "Hedef CIDR",
"Destination IP": "",
"Destination IP Address/Subnet": "",
"Destination Port": "",
"Destination Port/Port Range": "Hedef Ağ Adaptörü / Ağ Adaptör Aralığı",
"Detach": "Ayrıştır",
"Detach Instance": "Sanal Makineyi Ayrıştır",
@ -780,7 +796,9 @@
"Details": "Detaylar",
"Details *": "Detaylar *",
"Details about the PTR record.": "PTR kaydıyla ilgili detaylar.",
"Device": "",
"Device ID": "Cihaz Kimliği",
"Device ID/Name": "",
"Device Owner": "Cihaz Sahibi",
"Devicemapper": "Cihaz Haritalayıcı",
"Direct": "Doğrudan",
@ -894,10 +912,14 @@
"Edit host aggregate": "Ana Bilgisayar Kümesini Düzenle",
"Edit metadata": "Meta veriyi düzenle",
"Edit quota": "Kota düzenle",
"Edit rule": "",
"Editing only changes the content of the file, not the file name.": "Düzenleme, yalnızca dosyanın içeriğini değiştirir. Dosya adını değiştirmez.",
"Effective Mode": "Etkin Mod",
"Effective mode after configuration changes": "Yapılandırma değişikliklerinden sonra etkin mod",
"Egress": ıkış",
"Egress Policy": "",
"Egress Policy ID": "",
"Egress Policy Name": "",
"Egypt": "Mısır",
"Eject": ıkart",
"El Salvador": "El Salvador",
@ -1010,6 +1032,14 @@
"Fingerprint": "Parmak İzi",
"Finish Resize": "Yeniden Boyutlandırmayı Tamamla",
"Finland": "Finlandiya",
"Firewall": "",
"Firewall Detail": "",
"Firewall Policies": "",
"Firewall Policy": "",
"Firewall Port": "",
"Firewall Rule": "",
"Firewall Rules": "",
"Firewalls": "",
"Fixed IP": "Sabit IP",
"Fixed IP Address": "Sabit IP Adresi",
"Fixed IPs": "Sabit IP'ler",
@ -1132,6 +1162,9 @@
"Heterogeneous Computing": "Farklı Tipli Hesaplama",
"Hidden": "GİZLİ",
"Hide Advanced Options": "Gelişmiş Seçenekleri Gizle",
"Hide Default Firewalls": "",
"Hide Default Policies": "",
"Hide Default Rules": "",
"High Clock Speed": "Yüksek Saat Hızı",
"Home": "Ana Sayfa",
"Home page": "Ana Sayfa",
@ -1155,6 +1188,7 @@
"Hungary": "Macaristan",
"Hypervisor Detail": "Hypervisor Detayı",
"Hypervisors": "Hypervisor'lar",
"ICMP": "",
"ICMP Code": "ICMP Kodu",
"ICMP Type": "ICMP Türü",
"ICMP Type/ICMP Code": "ICMP Türü/ICMP Kodu",
@ -1263,6 +1297,9 @@
"Infinity": "Sonsuz",
"Info": "Bilgi",
"Ingress": "Giriş",
"Ingress Policy": "",
"Ingress Policy ID": "",
"Ingress Policy Name": "",
"Init Complete": "Başlatma Tamamlandı",
"Init Failed": "Başlatma Başarısız",
"Init In Progress": "Başlatma Devam Ediyor",
@ -1276,6 +1313,10 @@
"Input internal port or port range (example: 80 or 80:160)": "Dahili ağ adaptörünü veya ağ adaptörü aralığını girin (örneğin: 80 veya 80:160)",
"Input source port or port range (example: 80 or 80:160)": "Kaynak ağ adaptörünü veya ağ adaptörü aralığını girin (örneğin: 80 veya 80:160)",
"Insecure Registry": "Güvensiz Kayıt Defteri",
"Insert": "",
"Insert After": "",
"Insert Before": "",
"Insert Rule": "",
"Inspect Failed": "Denetim Başarısız",
"Inspecting": "Denetleniyor",
"Instance": "Sanal makine",
@ -1306,6 +1347,7 @@
"Instance ID": "Sanal Makine Kimliği",
"Instance IP": "Sanal Makine IP'si",
"Instance Info": "Sanal Makine Bilgisi",
"Instance Port": "",
"Instance Related": "Sanal Makine İlişkili",
"Instance Snapshot": "Sanal Makine Anlık Görüntü",
"Instance Snapshot Detail": "Sanal Makine Anlık Görüntü Ayrıntısı",
@ -1492,6 +1534,7 @@
"Manage Error": "Hata Yönetimi",
"Manage Host": "Ana Bilgisayarı Yönet",
"Manage Metadata": "Meta Verileri Yönet",
"Manage Ports": "",
"Manage QoS Spec": "QoS Belirtisini Yönet",
"Manage Resource Types": "Kaynak Türlerini Yönet",
"Manage Security Group": "Güvenlik Grubunu Yönet",
@ -1617,6 +1660,7 @@
"Network Dropped Packets": "Ağda Düşen Paketler",
"Network Errors": "Ağ Hataları",
"Network ID": "Ağ Kimliği",
"Network ID/Name": "",
"Network Info": "Ağ Bilgisi",
"Network Interface": "Ağ Arayüzü",
"Network Line": "Ağ Hattı",
@ -1903,7 +1947,10 @@
"Pointer Record": "İşaretçi Kaydı",
"Poland": "Polonya",
"Policy": "İlke",
"Policy Detail": "",
"Policy Edit": "",
"Policy Name": "İlke Adı",
"Policy Rules": "",
"Pool Algorithm": "Havuz Algoritması",
"Pool Description": "Havuz Açıklaması",
"Pool Detail": "Havuz Detayı",
@ -2013,6 +2060,7 @@
"RAM": "RAM",
"RAM (MiB)": "RAM (MiB)",
"RAW - Raw disk image format": "RAW - Ham disk imaj formatı",
"REJECT": "",
"RESTORE COMPLETE": "GERİ YÜKLEME TAMAMLANDI",
"RESUME COMPLETE": "DEVAM ET TAMAMLANDI",
"RESUME FAILED": "DEVAM ET BAŞARISIZ",
@ -2054,6 +2102,7 @@
"Recycle Bin": "Geri Dönüşüm Kutusu",
"Region": "Bölge",
"Registry Enabled": "Kayıt Defteri Etkinleştirildi",
"Related Policy": "",
"Related Resources": "İlgili Kaynaklar",
"Release": "Yayınla",
"Release Fixed IP": "Sabit IP Adresini Serbest Bırak",
@ -2065,6 +2114,7 @@
"Remove Default Project": "",
"Remove Network": "Ağı Kaldır",
"Remove Router": "Yönlendiriciyi Kaldır",
"Remove Rule": "",
"Remove default project for user": "",
"Rename": "Yeniden Adlandır",
"Rename is to copy the current file to the new file address and delete the current file, which will affect the creation time of the file.": "Yeniden adlandırma, mevcut dosyanın yeni dosya adresine kopyalanması ve mevcut dosyanın silinmesi anlamına gelir; bu, dosyanın oluşturma zamanını etkiler.",
@ -2144,7 +2194,12 @@
"Router Detail": "Yönlendirici Detayı",
"Router External": "Harici Yönlendirici",
"Router ID": "Yönlendirici ID'si",
"Router Port": "",
"Routers": "Yönlendiriciler",
"Rule": "",
"Rule Action": "",
"Rule Detail": "",
"Rule Edit": "",
"Rule Numbers": "Kural Numaraları",
"Rules": "Kurallar",
"Rules Number": "Kuralların Sayısı",
@ -2266,6 +2321,7 @@
"Shared Network": "Paylaşılan Ağ",
"Shared Networks": "Paylaşılan Ağlar",
"Shared QoS Policies": "Paylaşılan QoS İlkeleri",
"Shared policy only can insert shared rules.": "",
"Shares": "Paylaşımlar",
"Shelve": "Rafa Kaldır",
"Shelve Instance": "Sanal Makineyi Rafa Kaldır",
@ -2317,7 +2373,10 @@
"Somalia": "Somali",
"Sorry, the page you visited does not exist.": "Üzgünüz, ziyaret ettiğiniz sayfa mevcut değil.",
"Source": "Kaynak",
"Source IP": "",
"Source IP Address/Subnet": "",
"Source Path: {path}": "Kaynak Yolu: {path}",
"Source Port": "",
"Source Port/Port Range": "Kaynak Ağ Adaptörü/Ağ Adaptör Aralığı",
"South Africa": "Güney Afrika",
"South Korea": "Güney Kore",
@ -2810,7 +2869,11 @@
"Yemen": "Yemen",
"Yes": "Evet",
"Yes - Create a new system disk": "",
"You are not allowed to delete policy \"{ name }\" used by firewalls: { firewalls }.": "",
"You are not allowed to delete policy \"{ name }\".": "",
"You are not allowed to delete router \"{ name }\".": "\"{ name }\" adlı yönlendiriciyi silme izniniz yok.",
"You are not allowed to delete rule \"{ name }\" in use.": "",
"You are not allowed to delete rule \"{ name }\".": "",
"You are not allowed to delete snapshot \"{ name }\", which is used by creating volume \"{volumes}\".": "\"{volumes}\" adlı Diski oluştururken kullanılan \"{ name }\" adlı anlık görüntüyü silme izniniz yok.",
"You are not allowed to delete snapshot \"{ name }\".": "\"{ name }\" adlı anlık görüntüyü silme izniniz yok.",
"You are not allowed to jump to the console.": "Konsola geçiş yapma izniniz yok.",
@ -2874,6 +2937,7 @@
"create baremetal node": "bare metal düğümü oluştur",
"create default pool": "varsayılan havuz oluştur",
"create encryption": "şifreleme oluştur",
"create firewall policy": "",
"create flavor": "Şablon oluştur",
"create instance snapshot": "Sanal Makine anlık görüntüsü oluştur",
"create ipsec site connection": "IPSec site bağlantısı oluştur",
@ -2905,6 +2969,7 @@
"delete default pool": "varsayılan havuzu sil",
"delete domain": "alan adını sil",
"delete dscp marking rules": "DSCP işaretleme kurallarını sil",
"delete firewall": "",
"delete flavor": "Şabloni sil",
"delete group": "grubu sil",
"delete host": "",
@ -2918,11 +2983,13 @@
"delete load balancer": "yük dengeleyiciyi sil",
"delete member": "üyeyi sil",
"delete network": "ağı sil",
"delete policy": "",
"delete port forwarding": "ağ adaptörü yönlendirmeyi sil",
"delete project": "projei sil",
"delete qos policy": "QoS ilkesini sil",
"delete role": "rolü sil",
"delete router": "yönlendiriciyi sil",
"delete rule": "",
"delete segments": "",
"delete stack": "yığıyı sil",
"delete static route": "statik rotayı sil",
@ -2962,6 +3029,11 @@
"external port": "harici ağ adaptörü",
"external ports": "harici ağ adaptörleri",
"extra specs": "ek özellikler",
"firewall": "",
"firewall policies": "",
"firewall rule": "",
"firewall rules": "",
"firewalls": "",
"flavor": "Şablon",
"floating ip": "değişken ip",
"floating ips": "değişken ip'ler",
@ -2974,6 +3046,7 @@
"in": "giriş",
"ingress": "giriş",
"insert": "ekle",
"insert rule": "",
"instance": "Sanal Makine",
"instance snapshot": "Sanal Makine anlık görüntüsü",
"instance snapshots": "Sanal Makine anlık görüntüleri",
@ -2992,6 +3065,7 @@
"live migrate": "canlı taşı",
"load balancer": "yük dengeleyici",
"lock instance": "sanal makineyi kilitle",
"manage ports": "",
"manage qos spec": "QOS özelliğini yönet",
"manage resource types": "kaynak türlerini yönet",
"message": "mesaj",
@ -3012,6 +3086,7 @@
"phone": "telefon",
"please select network": "lütfen ağı seçin",
"please select subnet": "lütfen alt ağı seçin",
"policy": "",
"port": "ağ adaptörü",
"port forwarding": "ağ adaptörü yönlendirme",
"port forwardings": "ağ adaptörü yönlendirmeleri",
@ -3033,6 +3108,7 @@
"release fixed ip": "sabit ip'yi serbest bırak",
"remove network": "ağı kaldır",
"remove router": "yönlendiriciyi kaldır",
"remove rule": "",
"reserved_host": "",
"resize": "yeniden boyutlandır",
"resume instance": "sanal makineyi devam ettir",
@ -3116,6 +3192,8 @@
"vpn endpoint groups": "vpn uç nokta grupları",
"vpn services": "vpn hizmetleri",
"write": "yaz",
"{ name } Format Error (e.g. 192.168.1.1 or 192.168.1.1/24)": "",
"{ name } Format Error (e.g. FE80:0:0:0:0:0:0:1 or FE80:0:0:0:0:0:0:1/10)": "",
"{ size } GiB": "{ size } GiB",
"{ size } KiB": "{ size } KiB",
"{ size } MiB": "{ size } MiB",

View File

@ -34,6 +34,7 @@
"A dynamic scheduling algorithm that estimates the server load based on the number of currently active connections. The system allocates new connection requests to the server with the least number of current connections. Commonly used for long connection services, such as database connections and other services.": "通过当前活跃的连接数来估计服务器负载情况的一种动态调度算法,系统把新的连接请求分配给当前连接数目最少的服务器。常用于长连接服务,例如数据库连接等服务。",
"A host aggregate can be associated with at most one AZ. Once the association is established, the AZ cannot be disassociated.": "一个主机集合最多可以与一个AZ建立关联一旦建立了关联无法再取消关联AZ。",
"A public container will allow anyone to use the objects in your container through a public URL.": "一个公有容器会允许任何人通过公共 URL 去使用您容器里面的对象。",
"A rule specified before insertion or after insertion a rule. If both are not specified, the new rule is inserted as the first rule of the policy.": "指定在某条规则之前插入,或在某条规则之后插入。如果二者均未被指定,则插入到策略规则首位。",
"A snapshot is an image which preserves the disk state of a running instance, which can be used to start a new instance.": "云主机当前状态的磁盘数据保存,创建镜像文件,以备将来启动新的云主机使用。",
"A template is a YAML file that contains configuration information, please enter the correct format.": "模板是包含配置信息的YAML文件 请输入正确的格式。",
"A template is a YAML file that contains configuration information.": "模板是包含配置信息的YAML文件。",
@ -41,6 +42,7 @@
"ADOPT COMPLETE": "采用完成",
"AH": "",
"AKI - Amazon kernel image format": "AKI - Amazon 内核图像格式",
"ALLOW": "允许",
"AMI - Amazon server image format": "AMI - Amazon 服务器图像格式",
"ANY": "任何",
"API Address": "API地址",
@ -78,6 +80,7 @@
"Add Metadata": "添加元数据",
"Add NUMA Node": "添加NUMA节点",
"Add Network": "添加网络",
"Add Policy": "增加策略",
"Add Property": "添加属性",
"Add Router": "添加路由器",
"Add Virtual LAN": "添加虚拟网卡",
@ -174,6 +177,7 @@
"Associate Floating IP": "绑定浮动IP",
"Associate IP": "关联IP",
"Associate Network": "关联网络",
"Associated Ports": "关联端口",
"Associated QoS Spec ID": "关联QoS规格ID",
"Associated QoS Spec ID/Name": "关联QoS规格ID/名称",
"Associated Resource": "关联资源",
@ -193,6 +197,7 @@
"Attaching": "挂载中",
"Attachments Info": "挂载信息",
"Attributes": "属性",
"Audited": "已审计",
"Australia": "澳大利亚",
"Austria": "奥地利",
"Auth Algorithm": "授权算法",
@ -513,6 +518,8 @@
"Create Encryption": "创建加密",
"Create Extra Spec": "创建额外规格",
"Create Failed": "创建失败",
"Create Firewall": "创建防火墙",
"Create Firewall Policy": "创建防火墙策略",
"Create Flavor": "创建云主机类型",
"Create Folder": "创建文件夹",
"Create Host Aggregate": "创建主机集合",
@ -528,6 +535,7 @@
"Create Network": "创建网络",
"Create New Network": "创建新网络",
"Create Node": "注册节点",
"Create Policy": "创建策略",
"Create Port": "创建端口",
"Create Port Forwarding": "创建端口转发",
"Create Port Group": "创建端口组",
@ -567,6 +575,7 @@
"Create Volume Type": "创建云硬盘类型",
"Create Zone": "创建区域",
"Create a full backup, the system will automatically create a new backup chain, the full backup name is the backup chain name; Create an incremental backup, the system will automatically create an incremental backup under the newly created backup chain.": "创建全量备份后,系统会自动创建新的备份链,全量备份名称为备份链名称; 创建增量备份,系统将在新创建的备份链下的对应备份点自动创建增量备份。",
"Create firewall": "创建防火墙",
"Create host aggregate": "创建主机集合",
"Create image": "创建镜像",
"Create instance": "创建云主机",
@ -604,6 +613,7 @@
"Current Project Networks": "当前项目网络",
"Current Project QoS Policies": "当前项目QoS策略",
"Current QoS policy name": "当前QoS策略名称",
"Current Rules": "当前规则",
"Current Status": "当前状态",
"Current Storage Backend": "当前存储后端",
"Current data downloaded.": "当前数据已完成下载。",
@ -625,6 +635,7 @@
"DELETE COMPLETE": "删除完成",
"DELETE FAILED": "删除失败",
"DELETE_IN PROGRESS": "删除中",
"DENY": "丢弃",
"DHCP": "DHCP",
"DHCP Agent": "DHCP服务",
"DHCP Agents": "DHCP服务",
@ -699,6 +710,7 @@
"Delete Extra Specs": "删除额外规格",
"Delete Failed": "删除失败",
"Delete File": "删除文件",
"Delete Firewall": "删除防火墙",
"Delete Flavor": "删除云主机类型",
"Delete Folder": "删除文件夹",
"Delete Group": "删除用户组",
@ -715,6 +727,7 @@
"Delete Metadata": "删除元数据",
"Delete Network": "删除网络",
"Delete Node": "删除节点",
"Delete Policy": "删除策略",
"Delete Port": "删除端口",
"Delete Port Forwarding": "删除端口转发",
"Delete Port Group": "删除端口组",
@ -766,6 +779,9 @@
"Dest Folder": "目标文件夹",
"Destination": "目的地",
"Destination CIDR": "目的网络地址",
"Destination IP": "目的IP",
"Destination IP Address/Subnet": "目的IP地址/子网",
"Destination Port": "目的端口",
"Destination Port/Port Range": "目的端口/端口范围",
"Detach": "解绑",
"Detach Instance": "从云主机解绑",
@ -780,7 +796,9 @@
"Details": "详情",
"Details *": "详情 *",
"Details about the PTR record.": "有关 PTR 记录的详细信息。",
"Device": "设备",
"Device ID": "设备ID",
"Device ID/Name": "设备ID/名称",
"Device Owner": "设备所属者",
"Devicemapper": "设备映射",
"Direct": "方向",
@ -894,10 +912,14 @@
"Edit host aggregate": "编辑主机集合",
"Edit metadata": "编辑元数据",
"Edit quota": "编辑配额",
"Edit rule": "编辑规则",
"Editing only changes the content of the file, not the file name.": "编辑只改变文件内容,而不会改变文件名称。",
"Effective Mode": "生效模式",
"Effective mode after configuration changes": "配置变更后的生效模式",
"Egress": "出口",
"Egress Policy": "出口策略",
"Egress Policy ID": "出口策略ID",
"Egress Policy Name": "出口策略名称",
"Egypt": "埃及",
"Eject": "删除",
"El Salvador": "萨尔瓦多",
@ -1010,6 +1032,14 @@
"Fingerprint": "指纹",
"Finish Resize": "完成调整",
"Finland": "芬兰",
"Firewall": "防火墙",
"Firewall Detail": "防火墙详情",
"Firewall Policies": "防火墙策略",
"Firewall Policy": "防火墙策略",
"Firewall Port": "防火墙端口",
"Firewall Rule": "防火墙规则",
"Firewall Rules": "防火墙规则",
"Firewalls": "防火墙",
"Fixed IP": "内网IP",
"Fixed IP Address": "内网IP地址",
"Fixed IPs": "内网IP",
@ -1132,6 +1162,9 @@
"Heterogeneous Computing": "异构计算",
"Hidden": "隐藏",
"Hide Advanced Options": "隐藏高级选项",
"Hide Default Firewalls": "隐藏默认防火墙",
"Hide Default Policies": "隐藏默认策略",
"Hide Default Rules": "隐藏默认规则",
"High Clock Speed": "高主频型",
"Home": "首页",
"Home page": "首页",
@ -1155,6 +1188,7 @@
"Hungary": "匈牙利",
"Hypervisor Detail": "虚拟机管理器详情",
"Hypervisors": "虚拟机管理器",
"ICMP": "",
"ICMP Code": "ICMP编码",
"ICMP Type": "ICMP类型",
"ICMP Type/ICMP Code": "类型值/编码值",
@ -1263,6 +1297,9 @@
"Infinity": "无限制",
"Info": "信息",
"Ingress": "入口",
"Ingress Policy": "入口策略",
"Ingress Policy ID": "入口策略ID",
"Ingress Policy Name": "入口策略名称",
"Init Complete": "初始化完成",
"Init Failed": "初始化失败",
"Init In Progress": "正在初始化",
@ -1276,6 +1313,10 @@
"Input internal port or port range (example: 80 or 80:160)": "目标端口或端口范围例如80 或 80:160",
"Input source port or port range (example: 80 or 80:160)": "源端口或源端口范围(例如: 80 或 80:160)",
"Insecure Registry": "不安全的注册表",
"Insert": "插入",
"Insert After": "晚于",
"Insert Before": "早于",
"Insert Rule": "插入规则",
"Inspect Failed": "检查失败",
"Inspecting": "检查",
"Instance": "云主机",
@ -1306,6 +1347,7 @@
"Instance ID": "实例ID",
"Instance IP": "云主机IP",
"Instance Info": "云主机信息",
"Instance Port": "云主机端口",
"Instance Related": "云主机相关",
"Instance Snapshot": "云主机快照",
"Instance Snapshot Detail": "云主机快照详情",
@ -1492,6 +1534,7 @@
"Manage Error": "管理失败",
"Manage Host": "管理主机",
"Manage Metadata": "管理元数据",
"Manage Ports": "管理端口",
"Manage QoS Spec": "管理QoS规格",
"Manage Resource Types": "管理资源类型",
"Manage Security Group": "管理安全组",
@ -1617,6 +1660,7 @@
"Network Dropped Packets": "网络丢包率",
"Network Errors": "网络错误",
"Network ID": "网络ID",
"Network ID/Name": "网络ID/名称",
"Network Info": "网络信息",
"Network Interface": "网卡",
"Network Line": "网络线路",
@ -1903,7 +1947,10 @@
"Pointer Record": "指针记录",
"Poland": "波兰",
"Policy": "策略",
"Policy Detail": "策略详情",
"Policy Edit": "编辑策略",
"Policy Name": "策略名称",
"Policy Rules": "策略规则",
"Pool Algorithm": "资源池算法",
"Pool Description": "资源池描述",
"Pool Detail": "资源池详情",
@ -2013,6 +2060,7 @@
"RAM": "内存",
"RAM (MiB)": "内存 (MiB)",
"RAW - Raw disk image format": "RAW - 原始磁盘映像格式",
"REJECT": "拒绝",
"RESTORE COMPLETE": "恢复完成",
"RESUME COMPLETE": "恢复完成",
"RESUME FAILED": "恢复失败",
@ -2054,6 +2102,7 @@
"Recycle Bin": "回收站",
"Region": "域",
"Registry Enabled": "启用注册表",
"Related Policy": "关联策略",
"Related Resources": "关联资源",
"Release": "释放",
"Release Fixed IP": "释放内网IP",
@ -2065,6 +2114,7 @@
"Remove Default Project": "移除默认项目",
"Remove Network": "移除网络",
"Remove Router": "移除路由器",
"Remove Rule": "移除规则",
"Remove default project for user": "移除用户默认项目",
"Rename": "重命名",
"Rename is to copy the current file to the new file address and delete the current file, which will affect the creation time of the file.": "重命名是把当前文件复制到新文件地址,并删除当前文件,会影响文件的创建时间。",
@ -2144,7 +2194,12 @@
"Router Detail": "路由器详情",
"Router External": "外部网关",
"Router ID": "路由器ID",
"Router Port": "路由器端口",
"Routers": "路由器",
"Rule": "规则",
"Rule Action": "动作",
"Rule Detail": "规则详情",
"Rule Edit": "编辑规则",
"Rule Numbers": "规则数量",
"Rules": "规则",
"Rules Number": "规则数量",
@ -2266,6 +2321,7 @@
"Shared Network": "共享网络",
"Shared Networks": "共享网络",
"Shared QoS Policies": "共享QoS策略",
"Shared policy only can insert shared rules.": "共享的策略只可以插入共享的规则。",
"Shares": "共享",
"Shelve": "归档",
"Shelve Instance": "归档云主机",
@ -2317,7 +2373,10 @@
"Somalia": "索马里",
"Sorry, the page you visited does not exist.": "抱歉,您访问的页面不存在。",
"Source": "源",
"Source IP": "源IP",
"Source IP Address/Subnet": "源IP地址/子网",
"Source Path: {path}": "原路径:{path}",
"Source Port": "源端口",
"Source Port/Port Range": "源端口/端口范围",
"South Africa": "南非",
"South Korea": "韩国",
@ -2810,7 +2869,11 @@
"Yemen": "也门",
"Yes": "是",
"Yes - Create a new system disk": "是 - 创建新的系统盘",
"You are not allowed to delete policy \"{ name }\" used by firewalls: { firewalls }.": "无法删除防火墙:{ firewalls } 使用中的策略\"{ name }\"。",
"You are not allowed to delete policy \"{ name }\".": "无法删除策略\"{ name }\"。",
"You are not allowed to delete router \"{ name }\".": "无法删除路由器\"{ name }\"。",
"You are not allowed to delete rule \"{ name }\" in use.": "无法删除使用中的规则\"{ name }\"。",
"You are not allowed to delete rule \"{ name }\".": "无法删除规则\"{ name }\"。",
"You are not allowed to delete snapshot \"{ name }\", which is used by creating volume \"{volumes}\".": "无法删除创建了云硬盘 \"{volumes}\" 的快照 \"{ name }\"。",
"You are not allowed to delete snapshot \"{ name }\".": "无法删除快照\"{ name }\"。",
"You are not allowed to jump to the console.": "无法跳转到控制台。",
@ -2874,6 +2937,7 @@
"create baremetal node": "创建裸机节点",
"create default pool": "创建资源池",
"create encryption": "创建加密",
"create firewall policy": "创建防火墙策略",
"create flavor": "创建云主机类型",
"create instance snapshot": "创建云主机快照",
"create ipsec site connection": "创建IPsec站点连接",
@ -2905,6 +2969,7 @@
"delete default pool": "删除资源池",
"delete domain": "删除域",
"delete dscp marking rules": "删除DSCP标记规则",
"delete firewall": "删除防火墙",
"delete flavor": "删除云主机类型",
"delete group": "删除组",
"delete host": "删除主机",
@ -2918,11 +2983,13 @@
"delete load balancer": "删除负载均衡",
"delete member": "删除成员",
"delete network": "删除网络",
"delete policy": "删除策略",
"delete port forwarding": "删除端口转发",
"delete project": "删除项目",
"delete qos policy": "删除QoS策略",
"delete role": "删除角色",
"delete router": "删除路由器",
"delete rule": "删除规则",
"delete segments": "删除分组",
"delete stack": "删除stack",
"delete static route": "删除静态路由",
@ -2962,6 +3029,11 @@
"external port": "源端口",
"external ports": "源端口",
"extra specs": "额外规格",
"firewall": "防火墙",
"firewall policies": "防火墙策略",
"firewall rule": "防火墙规则",
"firewall rules": "防火墙规则",
"firewalls": "防火墙",
"flavor": "云主机类型",
"floating ip": "浮动IP",
"floating ips": "浮动IP",
@ -2974,6 +3046,7 @@
"in": "进",
"ingress": "入方向",
"insert": "插入",
"insert rule": "插入规则",
"instance": "云主机",
"instance snapshot": "云主机快照",
"instance snapshots": "云主机快照",
@ -2992,6 +3065,7 @@
"live migrate": "热迁移",
"load balancer": "负载均衡",
"lock instance": "锁定云主机",
"manage ports": "管理端口",
"manage qos spec": "管理QoS规格",
"manage resource types": "管理资源类型",
"message": "",
@ -3012,6 +3086,7 @@
"phone": "手机",
"please select network": "请选择网络",
"please select subnet": "请选择子网",
"policy": "策略",
"port": "端口",
"port forwarding": "端口转发",
"port forwardings": "端口转发",
@ -3033,6 +3108,7 @@
"release fixed ip": "释放内网IP",
"remove network": "移除网络",
"remove router": "移除路由器",
"remove rule": "移除规则",
"reserved_host": "保留主机",
"resize": "更改配置",
"resume instance": "恢复云主机",
@ -3116,6 +3192,8 @@
"vpn endpoint groups": "VPN端点组",
"vpn services": "VPN网关",
"write": "写",
"{ name } Format Error (e.g. 192.168.1.1 or 192.168.1.1/24)": "{ name }格式错误(例如192.168.1.1 或 192.168.1.1/24)",
"{ name } Format Error (e.g. FE80:0:0:0:0:0:0:1 or FE80:0:0:0:0:0:0:1/10)": "{ name }格式错误(例如FE80:0:0:0:0:0:0:1 或 FE80:0:0:0:0:0:0:1/10)",
"{ size } GiB": "{ size } GiB",
"{ size } KiB": "{ size } KiB",
"{ size } MiB": "{ size } MiB",

View File

@ -19,6 +19,7 @@ import { inject, observer } from 'mobx-react';
import globalVolumeTypeStore from 'stores/cinder/volume-type';
import globalProjectStore from 'stores/keystone/project';
import globalRootStore from 'stores/root';
import { firewallEndpoint } from 'client/client/constants';
import { isNumber } from 'lodash';
import styles from '../style.less';
@ -37,6 +38,23 @@ const keyPairTitle = (
</span>
);
const firewallQuota = firewallEndpoint()
? [
{
text: t('Firewalls'),
key: 'firewall_group',
},
{
text: t('Firewall Policies'),
key: 'firewall_policy',
},
{
text: t('Firewall Rules'),
key: 'firewall_rule',
},
]
: [];
export const quotaCardList = [
{
text: t('Compute'),
@ -86,6 +104,7 @@ export const quotaCardList = [
{ text: t('Ports'), key: 'port' },
{ text: t('Security Groups'), key: 'security_group' },
{ text: t('Security Group Rules'), key: 'security_group_rule' },
...firewallQuota,
],
},
];

View File

@ -0,0 +1,58 @@
// 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 { inject, observer } from 'mobx-react';
import { adminState } from 'resources/neutron/firewall';
import Base from 'containers/BaseDetail';
export class BaseDetail extends Base {
get leftCards() {
const cards = [this.baseInfoCard];
return cards;
}
get baseInfoCard() {
const options = [
{
label: t('Ingress Policy ID'),
dataIndex: 'ingress_firewall_policy_id',
},
{
label: t('Ingress Policy Name'),
dataIndex: 'ingress',
render: (value) => (value ? value.name : '-'),
},
{
label: t('Egress Policy ID'),
dataIndex: 'egress_firewall_policy_id',
},
{
label: t('Egress Policy Name'),
dataIndex: 'egress',
render: (value) => (value ? value.name : '-'),
},
{
label: t('Admin State'),
dataIndex: 'admin_state_up',
valueMap: adminState,
},
];
return {
title: t('Base Info'),
options,
};
}
}
export default inject('rootStore')(observer(BaseDetail));

View File

@ -0,0 +1,95 @@
// 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 { observer, inject } from 'mobx-react';
import Base from 'containers/List';
import { tableFilter } from 'resources/neutron/firewall-port';
import { PortStore } from 'stores/neutron/port';
import { portStatus } from 'resources/neutron/port';
export class Ports extends Base {
init() {
this.store = new PortStore();
}
get policy() {
return 'get_port';
}
get name() {
return t('ports');
}
get id() {
return this.params.id;
}
async getData({ silent, ...params } = {}) {
silent && (this.list.silent = true);
const { tab, ...rest } = params;
const { detail: { ports = [] } = {} } = this.props;
const newParams = { ports, ...rest };
if (this.isAdminPage) {
newParams.all_projects = true;
}
this.fetchListWithTry(async () => {
await this.store.fetchListByFirewall(newParams);
this.list.silent = false;
});
}
getColumns = () => [
{
title: t('ID/Name'),
dataIndex: 'name',
width: 150,
isLink: true,
routeName: this.getRouteName('firewallPortDetail'),
routeParamsFunc: (data) => ({
firewallId: this.id,
portId: data.id,
}),
},
{
title: t('Network ID/Name'),
dataIndex: 'network.name',
isLink: true,
routeName: this.getRouteName('networkDetail'),
idKey: 'network.id',
},
{
title: t('Owner'),
dataIndex: 'owner',
isHideable: true,
},
{
title: t('Device ID/Name'),
dataIndex: 'router.name',
isLink: true,
routeName: this.getRouteName('routerDetail'),
idKey: 'router.id',
},
{
title: t('Status'),
dataIndex: 'status',
valueMap: portStatus,
},
];
get searchFilters() {
return tableFilter;
}
}
export default inject('rootStore')(observer(Ports));

View File

@ -0,0 +1,36 @@
// 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 { inject, observer } from 'mobx-react';
import { PortDetail as Base } from 'pages/network/containers/Router/Port/Detail';
class PortDetail extends Base {
get listUrl() {
const { routerId, firewallId } = this.params;
if (routerId) {
return this.getRoutePath(
'routerDetail',
{ id: routerId },
{ tab: 'interfaces' }
);
}
return this.getRoutePath(
'firewallDetail',
{ id: firewallId },
{ tab: 'ports' }
);
}
}
export default inject('rootStore')(observer(PortDetail));

View File

@ -0,0 +1,86 @@
// 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 { inject, observer } from 'mobx-react';
import { FirewallStore } from 'stores/neutron/firewall';
import Base from 'containers/TabDetail';
import { firewallStatus } from 'resources/neutron/firewall';
import BaseDetail from './BaseDetail';
import Port from './Port';
import actionConfigs from '../actions';
export class FirewallDetail extends Base {
get name() {
return t('firewall');
}
get policy() {
return 'get_firewall_group';
}
get listUrl() {
return this.getRoutePath('firewall');
}
get actionConfigs() {
return this.isAdminPage
? actionConfigs.actionConfigsAdmin
: actionConfigs.actionConfigs;
}
get detailInfos() {
return [
{
title: t('Name'),
dataIndex: 'name',
},
{
title: t('Project ID'),
dataIndex: 'tenant_id',
hidden: !this.isAdminPage,
},
{
title: t('Status'),
dataIndex: 'status',
valueMap: firewallStatus,
},
{
title: t('Description'),
dataIndex: 'description',
},
];
}
get tabs() {
const tabs = [
{
title: t('Base Info'),
key: 'base',
component: BaseDetail,
},
{
title: t('Ports'),
key: 'ports',
component: Port,
},
];
return tabs;
}
init() {
this.store = new FirewallStore();
}
}
export default inject('rootStore')(observer(FirewallDetail));

View File

@ -0,0 +1,264 @@
// 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 { inject, observer } from 'mobx-react';
import { FormAction } from 'containers/Action';
import globalFirewallStore from 'stores/neutron/firewall';
import globalFirewallPolicyStore from 'stores/neutron/firewall-policy';
import globalRouterStore from 'stores/neutron/router';
import globalNetworkStore from 'stores/neutron/network';
import globalPortStore from 'stores/neutron/port';
import { tableOptions } from 'resources/neutron/firewall-policy';
import {
routerInterfaceOwners,
tableOptions as portTableOptions,
} from 'resources/neutron/firewall-port';
import { instancePortOptions } from 'resources/neutron/port';
import { toJS } from 'mobx';
import {
fetchNeutronQuota,
getQuotaInfo,
checkQuotaDisable,
} from 'resources/neutron/network';
const quotaKeys = ['firewall_group'];
const wishes = [1];
export class CreateForm extends FormAction {
init() {
this.store = globalFirewallStore;
this.policyStore = globalFirewallPolicyStore;
this.routerStore = globalRouterStore;
this.networkStore = globalNetworkStore;
this.portStore = globalPortStore;
this.getNetworks();
this.getRouters();
this.getPolicies();
this.getPorts();
fetchNeutronQuota(this);
}
static id = 'firewall-create';
static title = t('Create Firewall');
static path = '/network/firewall/create';
get listUrl() {
return this.getRoutePath('firewall');
}
get name() {
return t('Create firewall');
}
static policy = 'create_firewall_group';
static allowed() {
return Promise.resolve(true);
}
get disableSubmit() {
return checkQuotaDisable(quotaKeys, wishes);
}
get showQuota() {
return true;
}
get quotaInfo() {
return getQuotaInfo(this, quotaKeys, wishes);
}
getNetworks() {
this.networkStore.fetchList({ isFirewall: true });
}
getRouters() {
this.routerStore.fetchList({ isFirewall: true });
}
getPolicies() {
this.policyStore.fetchList();
}
async getPorts() {
this.portStore.fetchList({
device_owner: 'compute:nova',
project_id: this.currentProjectId,
});
this.updateDefaultValue();
}
get networks() {
return toJS(this.networkStore.list.data || []);
}
get ports() {
let ports = [];
(toJS(this.routerStore.list.data) || []).forEach((it) => {
const routerPorts = it.ports.filter((port) =>
routerInterfaceOwners.includes(port.device_owner)
);
routerPorts.forEach((port) => {
port.device_name = it.name;
port.owner = t('Router');
port.network = this.networks.find(
(network) => network.id === port.network_id
);
port.network_name = port.network ? port.network.name : '';
port.name = port.id;
port.ip_address = port.fixed_ips[0].ip_address;
port.subnet = port.network
? port.network.subnetDetails.find(
(subnet) => subnet.id === port.fixed_ips[0].subnet_id
)
: null;
port.subnet_name = (port.subnet && port.subnet.name) || '-';
});
ports = [...ports, ...routerPorts];
});
return ports;
}
get policies() {
return (this.policyStore.list.data || [])
.filter((it) => it.firewalls.length < 2)
.map((it) => ({
...it,
key: it.id,
}));
}
get defaultValue() {
return {
options: {
admin_state_up: true,
},
};
}
get instancePorts() {
return toJS(this.portStore.list.data || []).map((it) => ({
...it,
name: it.name || it.id,
}));
}
get portTabs() {
const routePortTab = {
title: t('Router Port'),
key: 'router',
props: {
data: this.ports,
...portTableOptions,
isLoading: this.routerStore.list.isLoading,
isMulti: true,
},
};
return [
{
title: t('Instance Port'),
key: 'instance',
props: {
data: this.instancePorts,
...instancePortOptions(this),
isLoading: this.portStore.list.isLoading,
isMulti: true,
},
},
routePortTab,
];
}
get formItems() {
return [
{
name: 'name',
label: t('Name'),
type: 'input-name',
required: true,
},
{
name: 'ingressPolicy',
label: t('Ingress Policy'),
type: 'select-table',
data: this.policies,
isLoading: this.policyStore.list.isLoading,
isMulti: false,
...tableOptions,
},
{
name: 'egressPolicy',
label: t('Egress Policy'),
type: 'select-table',
data: this.policies,
isLoading: this.policyStore.list.isLoading,
isMulti: false,
...tableOptions,
},
{
name: 'ports',
label: t('Ports'),
type: 'tab-select-table',
tabs: this.portTabs,
},
{
name: 'options',
label: t('Options'),
type: 'check-group',
options: [
{ label: t('Admin State'), value: 'admin_state_up' },
// { label: t('Shared'), value: 'shared' },
],
},
{
name: 'description',
label: t('Description'),
type: 'textarea',
},
];
}
onSubmit = (values) => {
const {
ingressPolicy = {},
egressPolicy = {},
options: { admin_state_up = true, shared = false } = {},
ports = {},
...rest
} = values;
const ingress =
ingressPolicy.selectedRowKeys && ingressPolicy.selectedRowKeys[0];
const egress =
egressPolicy.selectedRowKeys && egressPolicy.selectedRowKeys[0];
const body = {
admin_state_up,
shared,
...rest,
};
if (ingress) {
body.ingress_firewall_policy_id = ingress;
}
if (egress) {
body.egress_firewall_policy_id = egress;
}
if (ports.selectedRowKeys) {
body.ports = ports.selectedRowKeys;
}
return this.store.create(body);
};
}
export default inject('rootStore')(observer(CreateForm));

View File

@ -0,0 +1,62 @@
// 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 { ConfirmAction } from 'containers/Action';
import globalFirewallStore from 'stores/neutron/firewall';
import { isDefault, isMine, hasNoProject } from 'resources/neutron/firewall';
export default class DeleteAction extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Firewall');
}
get isDanger() {
return true;
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('delete firewall');
}
policy = 'delete_firewall_group';
allowedCheckFunc = (item) => {
if (!item) {
return true;
}
if (this.isAdminPage && hasNoProject(item)) {
return true;
}
return (
!isDefault(item) &&
this.isNotActive(item) &&
(isMine(item) || this.isAdminPage)
);
};
isNotActive = (item) => item.status !== 'ACTIVE';
onSubmit = (item) => {
const { id } = item || this.item;
return globalFirewallStore.delete({ id });
};
}

View File

@ -0,0 +1,155 @@
// 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 { inject, observer } from 'mobx-react';
import globalFirewallPolicyStore from 'stores/neutron/firewall-policy';
import globalFirewallStore from 'stores/neutron/firewall';
import { ModalAction } from 'containers/Action';
import { tableOptions } from 'resources/neutron/firewall-policy';
import { isDefault, isMine } from 'resources/neutron/firewall';
export class Edit extends ModalAction {
static id = 'edit';
static title = t('Edit');
init() {
this.store = globalFirewallStore;
this.policyStore = globalFirewallPolicyStore;
this.getPolicies();
}
static policy = 'update_firewall_group';
static get modalSize() {
return 'large';
}
getModalSize() {
return 'large';
}
async getPolicies() {
await this.policyStore.fetchList();
this.updateDefaultValue();
}
get policies() {
const { id } = this.item;
return (this.policyStore.list.data || [])
.filter((it) => {
if (it.firewalls.length < 2) {
return true;
}
const firewall = it.firewalls.find((tt) => tt.id === id);
return !!firewall;
})
.map((it) => ({
...it,
key: it.id,
}));
}
static allowed = (item) => Promise.resolve(!isDefault(item) && isMine(item));
get defaultValue() {
if (this.policies.length === 0) {
return {};
}
const {
ingress_firewall_policy_id,
egress_firewall_policy_id,
admin_state_up,
description,
} = this.item;
return {
name: this.item.name,
ingressPolicy: {
selectedRowKeys: ingress_firewall_policy_id
? [ingress_firewall_policy_id]
: [],
},
egressPolicy: {
selectedRowKeys: egress_firewall_policy_id
? [egress_firewall_policy_id]
: [],
},
options: {
admin_state_up,
},
description,
};
}
get formItems() {
return [
{
name: 'name',
label: t('Name'),
type: 'input',
required: true,
},
{
name: 'ingressPolicy',
label: t('Ingress Policy'),
type: 'select-table',
data: this.policies,
isLoading: this.policyStore.list.isLoading,
isMulti: false,
...tableOptions,
},
{
name: 'egressPolicy',
label: t('Egress Policy'),
type: 'select-table',
data: this.policies,
isLoading: this.policyStore.list.isLoading,
isMulti: false,
...tableOptions,
},
{
name: 'options',
label: t('Options'),
type: 'check-group',
options: [{ label: t('Admin State'), value: 'admin_state_up' }],
},
{
name: 'description',
label: t('Description'),
type: 'textarea',
},
];
}
onSubmit = (values) => {
const { id } = this.item;
const {
ingressPolicy,
egressPolicy,
options: { admin_state_up = true } = {},
...rest
} = values;
const ingress_firewall_policy_id = ingressPolicy.selectedRowKeys[0] || null;
const egress_firewall_policy_id = egressPolicy.selectedRowKeys[0] || null;
const body = {
admin_state_up,
ingress_firewall_policy_id,
egress_firewall_policy_id,
...rest,
};
return this.store.edit({ id }, body);
};
}
export default inject('rootStore')(observer(Edit));

View File

@ -0,0 +1,183 @@
// 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 { inject, observer } from 'mobx-react';
import globalRouterStore from 'stores/neutron/router';
import globalNetworkStore from 'stores/neutron/network';
import globalFirewallStore from 'stores/neutron/firewall';
import globalPortStore from 'stores/neutron/port';
import { ModalAction } from 'containers/Action';
import {
routerInterfaceOwners,
tableOptions,
} from 'resources/neutron/firewall-port';
import { instancePortOptions } from 'resources/neutron/port';
import { toJS } from 'mobx';
import { isMine } from 'resources/neutron/firewall';
export class ManagePort extends ModalAction {
static id = 'manage-port';
static title = t('Manage Ports');
init() {
this.store = globalFirewallStore;
this.routerStore = globalRouterStore;
this.networkStore = globalNetworkStore;
this.portStore = globalPortStore;
this.getNetworks();
this.getRouters();
this.getPorts();
}
static policy = 'update_firewall_group';
get name() {
return t('manage ports');
}
static get modalSize() {
return 'large';
}
getModalSize() {
return 'large';
}
async getNetworks() {
this.networkStore.fetchList({ isFirewall: true });
this.updateDefaultValue();
}
async getRouters() {
this.routerStore.fetchList({ isFirewall: true });
this.updateDefaultValue();
}
async getPorts() {
this.portStore.fetchList({
device_owner: 'compute:nova',
project_id: this.currentProjectId,
});
this.updateDefaultValue();
}
get networks() {
return toJS(this.networkStore.list.data || []);
}
get ports() {
let ports = [];
(toJS(this.routerStore.list.data) || []).forEach((it) => {
const routerPorts = it.ports.filter((port) =>
routerInterfaceOwners.includes(port.device_owner)
);
routerPorts.forEach((port) => {
port.device_name = it.name;
port.owner = t('Router');
port.network = this.networks.find(
(network) => network.id === port.network_id
);
port.router = it;
port.network_name = port.network ? port.network.name : '-';
port.name = port.id;
port.ip_address = port.fixed_ips[0].ip_address;
port.subnet = port.network
? port.network.subnetDetails.find(
(subnet) => subnet.id === port.fixed_ips[0].subnet_id
)
: null;
port.subnet_name = (port.subnet && port.subnet.name) || '-';
});
ports = [...ports, ...routerPorts];
});
return ports;
}
get instancePorts() {
return toJS(this.portStore.list.data || []).map((it) => ({
...it,
name: it.name || it.id,
}));
}
get portTabs() {
const routerPortTab = {
title: t('Router Port'),
key: 'router',
props: {
data: this.ports,
...tableOptions,
isLoading: this.routerStore.list.isLoading,
isMulti: true,
},
};
return [
{
title: t('Instance Port'),
key: 'instance',
props: {
data: this.instancePorts,
...instancePortOptions(this),
isLoading: this.portStore.list.isLoading,
isMulti: true,
},
},
routerPortTab,
];
}
static allowed = (item) => Promise.resolve(isMine(item));
get defaultValue() {
return {
name: this.item.name,
ports: {
selectedRows: this.item.ports.map((it) => ({
id: it,
name: it,
})),
selectedRowKeys: this.item.ports,
},
};
}
get formItems() {
return [
{
name: 'name',
label: t('Name'),
type: 'label',
iconType: 'firewall',
},
{
name: 'ports',
label: t('Ports'),
type: 'tab-select-table',
tabs: this.portTabs,
},
];
}
onSubmit = (values) => {
const { id } = this.item;
const { ports } = values;
const body = {
ports: ports.selectedRowKeys || null,
};
return this.store.edit({ id }, body);
};
}
export default inject('rootStore')(observer(ManagePort));

View File

@ -0,0 +1,45 @@
// 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 Create from './Create';
import Delete from './Delete';
import ManagePort from './ManagePort';
import Edit from './Edit';
const actionConfigs = {
rowActions: {
firstAction: Edit,
moreActions: [
{
action: ManagePort,
},
{
action: Delete,
},
],
},
batchActions: [Delete],
primaryActions: [Create],
};
const actionConfigsAdmin = {
rowActions: {
firstAction: Delete,
moreActions: [],
},
batchActions: [Delete],
primaryActions: [],
};
export default { actionConfigs, actionConfigsAdmin };

View File

@ -0,0 +1,139 @@
// 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 { observer, inject } from 'mobx-react';
import Base from 'containers/List';
import globalFirewallStore from 'stores/neutron/firewall';
import {
firewallStatus,
transitionStatus,
adminState,
getDefaultFilter,
} from 'resources/neutron/firewall';
import { getOptions } from 'utils/index';
import actionConfigs from './actions';
export class Firewall extends Base {
init() {
this.store = globalFirewallStore;
}
get policy() {
return 'get_firewall_group';
}
get name() {
return t('firewalls');
}
get hasTab() {
return true;
}
get actionConfigs() {
return this.isAdminPage
? actionConfigs.actionConfigsAdmin
: actionConfigs.actionConfigs;
}
get transitionStatusList() {
return transitionStatus;
}
get adminPageHasProjectFilter() {
return true;
}
get initFilter() {
return {
notDefault: true,
};
}
getColumns() {
return [
{
title: t('ID/Name'),
dataIndex: 'name',
routeName: this.getRouteName('firewallDetail'),
},
{
title: t('Project ID/Name'),
dataIndex: 'project_name',
hidden: !this.isAdminPage,
isHideable: true,
},
{
title: t('Description'),
dataIndex: 'description',
isHideable: true,
},
{
title: t('Ingress Policy'),
dataIndex: 'ingressPolicyName',
isLink: true,
routeName: this.getRouteName('firewallPolicyDetail'),
idKey: 'ingress_firewall_policy_id',
isHideable: true,
},
{
title: t('Egress Policy'),
dataIndex: 'egressPolicyName',
isLink: true,
routeName: this.getRouteName('firewallPolicyDetail'),
idKey: 'egress_firewall_policy_id',
isHideable: true,
},
{
title: t('Associated Ports'),
dataIndex: 'ports',
render: (value) => value.length,
isHideable: true,
},
{
title: t('Status'),
dataIndex: 'status',
valueMap: firewallStatus,
},
{
title: t('Admin State'),
dataIndex: 'admin_state_up',
valueMap: adminState,
},
];
}
get searchFilters() {
return [
{
label: t('Name'),
name: 'name',
},
{
label: t('Status'),
name: 'status',
options: getOptions(firewallStatus),
include: false,
},
{
label: t('Admin State'),
name: 'admin_state_up',
options: getOptions(adminState),
},
getDefaultFilter(t('Hide Default Firewalls')),
];
}
}
export default inject('rootStore')(observer(Firewall));

View File

@ -0,0 +1,83 @@
// 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 { inject, observer } from 'mobx-react';
import { FirewallPolicyStore } from 'stores/neutron/firewall-policy';
import Base from 'containers/TabDetail';
import Rule from '../../Rule';
import actionConfigs from '../actions';
export class InstanceDetail extends Base {
get name() {
return t('policy');
}
get policy() {
return 'get_firewall_policy';
}
get listUrl() {
return this.getRoutePath('firewall', null, { tab: 'policies' });
}
get actionConfigs() {
return this.isAdminPage
? actionConfigs.actionConfigsAdmin
: actionConfigs.actionConfigs;
}
get detailInfos() {
return [
{
title: t('Name'),
dataIndex: 'name',
},
{
title: t('Project ID'),
dataIndex: 'project_id',
},
{
title: t('Description'),
dataIndex: 'description',
},
{
title: t('Shared'),
dataIndex: 'shared',
valueRender: 'yesNo',
},
{
title: t('Audited'),
dataIndex: 'audited',
valueRender: 'yesNo',
},
];
}
get tabs() {
const tabs = [
{
title: t('Policy Rules'),
key: 'rules',
component: Rule,
},
];
return tabs;
}
init() {
this.store = new FirewallPolicyStore();
}
}
export default inject('rootStore')(observer(InstanceDetail));

View File

@ -0,0 +1,114 @@
// 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 { inject, observer } from 'mobx-react';
import globalFirewallPolicyStore from 'stores/neutron/firewall-policy';
import { ModalAction } from 'containers/Action';
import { checkPolicyRule } from 'resources/skyline/policy';
import {
fetchNeutronQuota,
getQuotaInfo,
checkQuotaDisable,
} from 'resources/neutron/network';
const quotaKeys = ['firewall_policy'];
const wishes = [1];
export class Create extends ModalAction {
static id = 'create-simple';
static title = t('Create Firewall Policy');
static buttonText = t('Create Policy');
get name() {
return t('create firewall policy');
}
init() {
this.store = globalFirewallPolicyStore;
fetchNeutronQuota(this);
}
static policy = 'create_firewall_policy';
static allowed = () => Promise.resolve(true);
static get disableSubmit() {
return checkQuotaDisable(quotaKeys, wishes);
}
static get showQuota() {
return true;
}
get showQuota() {
return true;
}
get quotaInfo() {
return getQuotaInfo(this, quotaKeys, wishes);
}
get defaultValue() {
return {
options: {
shared: false,
audited: false,
},
};
}
get formItems() {
return [
{
name: 'name',
label: t('Name'),
type: 'input',
required: true,
},
{
name: 'options',
label: t('Options'),
type: 'check-group',
options: [
{
label: t('Shared'),
value: 'shared',
disabled: !checkPolicyRule('update_firewall_policy:shared'),
},
{ label: t('Audited'), value: 'audited' },
],
},
{
name: 'description',
label: t('Description'),
type: 'textarea',
},
];
}
onSubmit = (values) => {
const { name, description, options: { shared, audited } = {} } = values;
const body = {
name,
shared,
audited,
description,
};
return this.store.create(body);
};
}
export default inject('rootStore')(observer(Create));

View File

@ -0,0 +1,75 @@
// 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 { ConfirmAction } from 'containers/Action';
import { isArray } from 'lodash';
import globalFirewallPolicyStore from 'stores/neutron/firewall-policy';
import { isMine } from 'resources/neutron/firewall-policy';
export default class DeleteAction extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Policy');
}
get isDanger() {
return true;
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('delete policy');
}
policy = 'delete_firewall_policy';
allowedCheckFunc = (item) => {
if (!item) {
return true;
}
return this.isNotUse(item) && (isMine(item) || this.isAdminPage);
};
performErrorMsg = (failedItems) => {
const item = isArray(failedItems) ? failedItems[0] : failedItems;
let errorMsg = t('You are not allowed to delete policy "{ name }".', {
name: item.name,
});
if (!this.isNotUse(item)) {
errorMsg = t(
'You are not allowed to delete policy "{ name }" used by firewalls: { firewalls }.',
{
name: item.name,
firewalls: item.firewalls.map((it) => it.name).join(', '),
}
);
}
return errorMsg;
};
isNotUse(item) {
return item.firewalls.length === 0;
}
onSubmit = (item) => {
const { id } = item || this.item;
return globalFirewallPolicyStore.delete({ id });
};
}

View File

@ -0,0 +1,94 @@
// 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 { inject, observer } from 'mobx-react';
import globalFirewallPolicyStore from 'stores/neutron/firewall-policy';
import { ModalAction } from 'containers/Action';
import { isDefault, isMine } from 'resources/neutron/firewall-policy';
import { checkPolicyRule } from 'resources/skyline/policy';
export class Edit extends ModalAction {
static id = 'edit';
static title = t('Edit');
init() {
this.store = globalFirewallPolicyStore;
}
static policy = 'update_firewall_policy';
static allowed = (item) => !isDefault(item) && isMine(item);
get defaultValue() {
const { name, shared, description, audited } = this.item;
return {
name,
description,
options: {
shared,
audited,
},
};
}
canChangeShared() {
return checkPolicyRule('update_firewall_policy:shared');
}
get formItems() {
return [
{
name: 'name',
label: t('Name'),
type: 'input',
required: true,
},
{
name: 'options',
label: t('Options'),
type: 'check-group',
options: [
{
label: t('Shared'),
value: 'shared',
disabled: !this.canChangeShared(),
},
{ label: t('Audited'), value: 'audited' },
],
},
{
name: 'description',
label: t('Description'),
type: 'textarea',
},
];
}
onSubmit = (values) => {
const { id } = this.item;
const { name, options: { shared, audited } = {}, description } = values;
const body = {
name,
audited,
description,
};
if (this.canChangeShared()) {
body.shared = shared;
}
return this.store.edit({ id }, body);
};
}
export default inject('rootStore')(observer(Edit));

View File

@ -0,0 +1,162 @@
// 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 { inject, observer } from 'mobx-react';
import globalFirewallPolicyStore from 'stores/neutron/firewall-policy';
import globalFirewallRuleStore from 'stores/neutron/firewall-rule';
import { ModalAction } from 'containers/Action';
import { tableOptions, isMine } from 'resources/neutron/firewall-rule';
export class Edit extends ModalAction {
static id = 'edit';
static title = t('Insert Rule');
init() {
this.store = globalFirewallPolicyStore;
this.ruleStore = globalFirewallRuleStore;
this.getRules();
}
static policy = 'update_firewall_policy';
get name() {
return t('insert rule');
}
static get modalSize() {
return 'large';
}
getModalSize() {
return 'large';
}
getRules() {
this.ruleStore.fetchList();
}
get rules() {
return (this.ruleStore.list.data || [])
.filter((it) => this.currentRules.every((rule) => rule.id !== it.id))
.filter((it) => {
if (this.item.shared) {
return it.shared;
}
return true;
})
.map((it) => ({
...it,
key: it.id,
}));
}
get currentRules() {
const { rules } = this.item;
return rules;
}
static allowed = (item) => Promise.resolve(isMine(item));
get defaultValue() {
const { name } = this.item;
return {
name,
insert: 'before',
};
}
get insertTypes() {
return [
{
label: t('Insert Before'),
value: 'before',
},
{
label: t('Insert After'),
value: 'after',
},
];
}
get tips() {
const tip = t(
'A rule specified before insertion or after insertion a rule. If both are not specified, the new rule is inserted as the first rule of the policy.'
);
if (this.item.shared) {
return tip + t('Shared policy only can insert shared rules.');
}
return tip;
}
get formItems() {
return [
{
name: 'name',
label: t('Name'),
type: 'label',
iconType: 'policy',
},
{
name: 'rule',
label: t('Rule'),
type: 'select-table',
required: true,
data: this.rules,
isLoading: this.ruleStore.list.isLoading,
isMulti: false,
...tableOptions,
},
{
name: 'insert',
label: t('Insert'),
type: 'radio',
options: this.insertTypes,
required: true,
},
{
name: 'current',
label: t('Current Rules'),
type: 'select-table',
data: this.currentRules,
isMulti: false,
...tableOptions,
},
];
}
onSubmit = (values) => {
const { id } = this.item;
const { rule, insert, current: { selectedRowKeys = [] } = {} } = values;
const firewall_rule_id = rule.selectedRowKeys[0];
let insertAfter = '';
let insertBefore = '';
if (selectedRowKeys.length > 0) {
if (insert === 'before') {
insertBefore = selectedRowKeys[0];
} else {
insertAfter = selectedRowKeys[0];
}
}
const body = {
firewall_policy_id: id,
firewall_rule_id,
insert_before: insertBefore,
insert_after: insertAfter,
};
return this.store.insertRule({ id }, body);
};
}
export default inject('rootStore')(observer(Edit));

View File

@ -0,0 +1,81 @@
// 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 { inject, observer } from 'mobx-react';
import globalFirewallPolicyStore from 'stores/neutron/firewall-policy';
import { ModalAction } from 'containers/Action';
import { tableOptions, isMine } from 'resources/neutron/firewall-rule';
export class Edit extends ModalAction {
static id = 'edit';
static title = t('Remove Rule');
static policy = 'update_firewall_policy';
get name() {
return t('remove rule');
}
static get modalSize() {
return 'large';
}
getModalSize() {
return 'large';
}
get rules() {
const { rules } = this.item;
return rules;
}
static allowed = (item) => Promise.resolve(isMine(item));
get defaultValue() {
return {
name: this.item.name,
};
}
get formItems() {
return [
{
name: 'name',
label: t('Name'),
type: 'label',
iconType: 'policy',
},
{
name: 'rule',
label: t('Rules'),
type: 'select-table',
required: true,
data: this.rules,
isMulti: false,
...tableOptions,
},
];
}
onSubmit = (values) => {
const { id } = this.item;
const { rule } = values;
const body = {
firewall_rule_id: rule.selectedRowKeys[0],
};
return globalFirewallPolicyStore.removeRule({ id }, body);
};
}
export default inject('rootStore')(observer(Edit));

View File

@ -0,0 +1,49 @@
// 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 Create from './Create';
import Delete from './Delete';
import Edit from './Edit';
import InsertRule from './InsertRule';
import RemoveRule from './RemoveRule';
const actionConfigs = {
rowActions: {
firstAction: Edit,
moreActions: [
{
action: InsertRule,
},
{
action: RemoveRule,
},
{
action: Delete,
},
],
},
batchActions: [Delete],
primaryActions: [Create],
};
const actionConfigsAdmin = {
rowActions: {
firstAction: Delete,
moreActions: [],
},
batchActions: [Delete],
primaryActions: [],
};
export default { actionConfigs, actionConfigsAdmin };

View File

@ -0,0 +1,148 @@
// 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 { observer, inject } from 'mobx-react';
import Base from 'containers/List';
import globalFirewallPolicyStore from 'stores/neutron/firewall-policy';
import { yesNoOptions } from 'utils/constants';
import { getDefaultFilter } from 'resources/neutron/firewall';
import actionConfigs from './actions';
export class Policy extends Base {
init() {
this.store = globalFirewallPolicyStore;
}
get policy() {
return 'get_firewall_policy';
}
get name() {
return t('firewall policies');
}
get hasTab() {
return true;
}
get actionConfigs() {
return this.isAdminPage
? actionConfigs.actionConfigsAdmin
: actionConfigs.actionConfigs;
}
get adminPageHasProjectFilter() {
return true;
}
getColumns() {
return [
{
title: t('ID/Name'),
dataIndex: 'name',
routeName: this.getRouteName('firewallPolicyDetail'),
},
{
title: t('Project ID/Name'),
dataIndex: 'project_name',
hidden: !this.isAdminPage,
isHideable: true,
},
{
title: t('Description'),
dataIndex: 'description',
isHideable: true,
},
{
title: t('Rules'),
dataIndex: 'rules',
isHideable: true,
render: (value) => {
if (!value || value.length === 0) {
return '-';
}
return value.map((item) => (
<div key={item.id}>
{this.getLinkRender('firewallRuleDetail', item.name, {
id: item.id,
})}
</div>
));
},
stringify: (value) => {
if (!value || value.length === 0) {
return '-';
}
return value.map((item) => item.name).join(', ');
},
},
{
title: t('Firewalls'),
dataIndex: 'firewalls',
isHideable: true,
render: (value) => {
if (!value || value.length === 0) {
return '-';
}
return value.map((item) => (
<div key={item.id}>
{this.getLinkRender('firewallDetail', item.name, { id: item.id })}
</div>
));
},
stringify: (value) => {
if (!value || value.length === 0) {
return '-';
}
return value.map((item) => item.name).join(', ');
},
},
{
title: t('Shared'),
dataIndex: 'shared',
valueRender: 'yesNo',
width: 80,
},
{
title: t('Audited'),
dataIndex: 'audited',
valueRender: 'yesNo',
width: 100,
},
];
}
get searchFilters() {
return [
{
label: t('Name'),
name: 'name',
},
{
label: t('Shared'),
name: 'shared',
options: yesNoOptions,
},
{
label: t('Audited'),
name: 'audited',
options: yesNoOptions,
},
getDefaultFilter(t('Hide Default Policies')),
];
}
}
export default inject('rootStore')(observer(Policy));

View File

@ -0,0 +1,48 @@
// 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 { inject, observer } from 'mobx-react';
import { tableColumns } from 'resources/neutron/firewall-rule';
import Base from 'containers/BaseDetail';
export class BaseDetail extends Base {
get leftCards() {
return [this.baseInfoCard];
}
get baseInfoCard() {
const columns = tableColumns.slice(3, tableColumns.length).map((it) => ({
...it,
label: it.title,
}));
const policy = {
label: t('Related Policy'),
dataIndex: 'policies',
render: (value) =>
(value || []).map((it) => (
<div key={it.id}>
{this.getLinkRender('firewallPolicyDetail', it.name, { id: it.id })}
</div>
)),
};
const options = [policy, ...columns];
return {
title: t('Base Info'),
options,
};
}
}
export default inject('rootStore')(observer(BaseDetail));

View File

@ -0,0 +1,79 @@
// 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 { inject, observer } from 'mobx-react';
import { FirewallRuleStore } from 'stores/neutron/firewall-rule';
import Base from 'containers/TabDetail';
import BaseDetail from './BaseDetail';
import actionConfigs from '../actions';
export class FirewallRuleDetail extends Base {
get name() {
return t('firewall rule');
}
get policy() {
return 'get_firewall_rule';
}
get listUrl() {
return this.getRoutePath('firewall', null, { tab: 'rules' });
}
get actionConfigs() {
if (this.isAdminPage) {
return actionConfigs.actionConfigsAdmin;
}
return actionConfigs.actionConfigs;
}
get detailInfos() {
return [
{
title: t('Name'),
dataIndex: 'name',
},
{
title: t('Project ID'),
dataIndex: 'tenant_id',
},
{
title: t('Description'),
dataIndex: 'description',
},
];
}
get tabs() {
const tabs = [
{
title: t('Base Info'),
key: 'base',
component: BaseDetail,
},
// {
// title: t('Rules'),
// key: 'rules',
// component: members,
// },
];
return tabs;
}
init() {
this.store = new FirewallRuleStore();
}
}
export default inject('rootStore')(observer(FirewallRuleDetail));

View File

@ -0,0 +1,289 @@
// 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 { inject, observer } from 'mobx-react';
import { FormAction } from 'containers/Action';
import { isUndefined } from 'lodash';
import globalFirewallRuleStore, {
FirewallRuleStore,
} from 'stores/neutron/firewall-rule';
import { actionInfos, protocolInfos } from 'resources/neutron/firewall-rule';
import { ipValidate } from 'utils/validate';
import { toJS } from 'mobx';
import { checkPolicyRule } from 'resources/skyline/policy';
import {
fetchNeutronQuota,
getQuotaInfo,
checkQuotaDisable,
} from 'resources/neutron/network';
const quotaKeys = ['firewall_rule'];
const wishes = [1];
const { isIpCidr, isIPv6Cidr, isIPv4, isIpv6 } = ipValidate;
export class CreateForm extends FormAction {
init() {
this.store = new FirewallRuleStore();
this.getDetail();
fetchNeutronQuota(this);
}
static id = 'rule-create';
static title = t('Create Rule');
static path = '/network/firewall-rule/create';
get listUrl() {
return this.getRoutePath('firewall', null, { tab: 'rules' });
}
get isEdit() {
return this.params && !!this.params.id;
}
get name() {
return this.isEdit ? t('Edit rule') : t('Create rule');
}
get id() {
return this.params.id;
}
static policy = 'create_firewall_rule';
static allowed() {
return Promise.resolve(true);
}
get disableSubmit() {
if (this.isEdit) {
return false;
}
return checkQuotaDisable(quotaKeys, wishes);
}
get showQuota() {
return !this.isEdit;
}
get quotaInfo() {
return getQuotaInfo(this, quotaKeys, wishes);
}
get defaultValue() {
if (this.id) {
const data = toJS(this.store.detail);
return {
...data,
options: {
enabled: data.enabled,
shared: data.shared,
},
};
}
return {
protocol: 'tcp',
action: 'allow',
ip_version: 4,
options: {
enabled: true,
shared: false,
},
};
}
get protocolList() {
return Object.keys(protocolInfos).map((key) => ({
value: key,
label: protocolInfos[key],
}));
}
get actionList() {
return Object.keys(actionInfos).map((key) => ({
value: key,
label: actionInfos[key],
}));
}
get ipVersionList() {
return [
{ value: 4, label: t('IPv4') },
{ value: 6, label: t('IPv6') },
];
}
async getDetail() {
if (this.params.id) {
await this.store.fetchDetail(this.params);
this.updateDefaultValue();
this.updateState();
}
}
checkIp = (type) => (rule, value) => {
const name = type === 'source' ? t('Source IP') : t('Destination IP');
if (!value) {
return Promise.resolve();
}
const ip = value.trim();
if (isUndefined(value) || ip.length === 0) {
return Promise.resolve();
}
const { ip_version = 4 } = this.state;
const isIpv4 = ip_version === 4;
if (isIpv4 && !(isIpCidr(ip) || isIPv4(ip))) {
return Promise.reject(
t('{ name } Format Error (e.g. 192.168.1.1 or 192.168.1.1/24)', {
name,
})
);
}
if (!isIpv4 && !(isIPv6Cidr(ip) || isIpv6(ip))) {
return Promise.reject(
t(
'{ name } Format Error (e.g. FE80:0:0:0:0:0:0:1 or FE80:0:0:0:0:0:0:1/10)',
{ name }
)
);
}
return Promise.resolve();
};
checkSourceIp = () => this.checkIp('source');
checkDestinationIp = () => this.checkIp('destination');
canChangeShared = () => {
const result = checkPolicyRule('update_firewall_rule:shared');
if (!result) {
return false;
}
if (this.id) {
const detail = toJS(this.store.detail);
return (detail.policies || []).every((it) => !it.shared);
}
return true;
};
get formItems() {
const { protocol } = this.state;
return [
{
name: 'name',
label: t('Name'),
type: 'input',
// type: 'input-name',
required: true,
},
{
name: 'protocol',
label: t('Protocol'),
type: 'radio',
options: this.protocolList,
required: true,
},
{
name: 'action',
label: t('Rule Action'),
type: 'select',
options: this.actionList,
required: true,
},
{
name: 'ip_version',
label: t('IP Version'),
type: 'radio',
options: this.ipVersionList,
},
{
name: 'source_ip_address',
label: t('Source IP Address/Subnet'),
type: 'input',
validator: this.checkSourceIp(),
},
{
name: 'source_port',
label: t('Source Port/Port Range'),
// TODO
type: 'port-range',
hidden: ['any', 'icmp'].includes(protocol),
},
{
name: 'destination_ip_address',
label: t('Destination IP Address/Subnet'),
type: 'input',
validator: this.checkDestinationIp(),
},
{
name: 'destination_port',
label: t('Destination Port/Port Range'),
type: 'input',
help: t('Input destination port or port range (example: 80 or 80:160)'),
hidden: ['any', 'icmp'].includes(protocol),
},
{
name: 'options',
label: t('Options'),
type: 'check-group',
options: [
{ label: t('Enabled'), value: 'enabled' },
{
label: t('Shared'),
value: 'shared',
disabled: !this.canChangeShared(),
},
],
},
{
name: 'description',
label: t('Description'),
type: 'textarea',
},
];
}
onSubmit = (values) => {
const {
options: { enabled, shared },
protocol,
destination_ip_address,
source_ip_address,
source_port,
destination_port,
...rest
} = values;
const body = {
...rest,
enabled,
// shared,
protocol: protocol === 'any' ? null : protocol,
destination_ip_address: destination_ip_address || null,
source_ip_address: source_ip_address || null,
source_port: source_port || null,
destination_port: destination_port || null,
};
if (this.canChangeShared()) {
body.shared = shared;
}
if (!this.id) {
return globalFirewallRuleStore.create(body);
}
return globalFirewallRuleStore.edit({ id: this.id }, body);
};
}
export default inject('rootStore')(observer(CreateForm));

View File

@ -0,0 +1,71 @@
// 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 { ConfirmAction } from 'containers/Action';
import { isArray } from 'lodash';
import globalFirewallRuleStore from 'stores/neutron/firewall-rule';
import { isMine } from 'resources/neutron/firewall-rule';
export default class DeleteAction extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Rule');
}
get isDanger() {
return true;
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('delete rule');
}
policy = 'delete_firewall_rule';
allowedCheckFunc = (item) => {
if (!item) {
return true;
}
return this.isNotUse(item) && (isMine(item) || this.isAdminPage);
};
performErrorMsg = (failedItems) => {
const item = isArray(failedItems) ? failedItems[0] : failedItems;
let errorMsg = t('You are not allowed to delete rule "{ name }".', {
name: item.name,
});
if (!this.isNotUse(item)) {
errorMsg = t('You are not allowed to delete rule "{ name }" in use.', {
name: item.name,
});
}
return errorMsg;
};
isNotUse(item) {
return item.policies.length === 0;
}
onSubmit = (item) => {
const { id } = item || this.item;
return globalFirewallRuleStore.delete({ id });
};
}

View File

@ -0,0 +1,43 @@
// 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 { inject, observer } from 'mobx-react';
import { isMine } from 'resources/neutron/firewall-rule';
import Create from './Create';
export class EditForm extends Create {
static id = 'rule-edit';
static title = t('Edit Rule');
static buttonText = t('Edit');
static path = (item) => `/network/firewall-rule/edit/${item.id}`;
get listUrl() {
return this.getRoutePath('firewall', null, { tab: 'rules' });
}
get name() {
return t('Edit rule');
}
static policy = 'update_firewall_rule';
static allowed(item) {
return Promise.resolve(isMine(item));
}
}
export default inject('rootStore')(observer(EditForm));

View File

@ -0,0 +1,50 @@
// 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 Create from './Create';
import Edit from './Edit';
import Delete from './Delete';
const actionConfigs = {
rowActions: {
firstAction: Edit,
moreActions: [
{
action: Delete,
},
],
},
batchActions: [Delete],
primaryActions: [Create],
};
const actionConfigsInDetail = {
rowActions: {
firstAction: Edit,
moreActions: [],
},
batchActions: [],
primaryActions: [],
};
const actionConfigsAdmin = {
rowActions: {
firstAction: Delete,
moreActions: [],
},
batchActions: [Delete],
primaryActions: [],
};
export default { actionConfigs, actionConfigsInDetail, actionConfigsAdmin };

View File

@ -0,0 +1,115 @@
// 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 { observer, inject } from 'mobx-react';
import Base from 'containers/List';
import { FirewallRuleStore } from 'stores/neutron/firewall-rule';
import { tableFilter, tableColumns } from 'resources/neutron/firewall-rule';
import { emptyActionConfig } from 'utils/constants';
import actionConfigs from './actions';
export class Rule extends Base {
init() {
this.store = new FirewallRuleStore();
}
get policy() {
return 'get_firewall_rule';
}
get name() {
return t('firewall rules');
}
get hasTab() {
return true;
}
get actionConfigs() {
if (this.isAdminPage) {
return this.inDetailPage
? emptyActionConfig
: actionConfigs.actionConfigsAdmin;
}
return this.inDetailPage
? actionConfigs.actionConfigsInDetail
: actionConfigs.actionConfigs;
}
get adminPageHasProjectFilter() {
return true;
}
getColumns() {
const nameColumn = {
title: t('ID/Name'),
dataIndex: 'name',
routeName: this.getRouteName('firewallRuleDetail'),
};
const projectColumn = {
title: t('Project ID/Name'),
dataIndex: 'project_name',
hidden: !this.isAdminPage,
isHideable: true,
};
const policyColumn = {
title: t('Related Policy'),
dataIndex: 'policies',
isHideable: true,
render: (value) =>
value.map((it) => (
<div key={it.id}>
{this.getLinkRender('firewallPolicyDetail', it.name, { id: it.id })}
</div>
)),
stringify: (value) => value.map((it) => it.name).join(','),
};
const columns = [
nameColumn,
projectColumn,
...tableColumns.slice(1, 2),
policyColumn,
...tableColumns.slice(2, tableColumns.length),
];
return columns;
}
get searchFilters() {
return tableFilter;
}
async getData({ silent, ...params } = {}) {
const { detail: { firewall_rules = [] } = {}, match } = this.props;
const { path } = match;
const newParams = { ...params };
if (this.isAdminPage) {
newParams.all_projects = true;
}
silent && (this.list.silent = true);
this.fetchListWithTry(async () => {
if (path.indexOf('firewall-policy') >= 0) {
newParams.firewall_rules = firewall_rules;
await this.store.fetchListByPolicy(newParams);
} else {
await this.store.fetchList(newParams);
}
this.list.silent = false;
});
}
}
export default inject('rootStore')(observer(Rule));

View File

@ -0,0 +1,57 @@
// 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 { observer, inject } from 'mobx-react';
import Base from 'containers/TabList';
import { firewallEndpoint } from 'client/client/constants';
import Firewall from './Firewall';
import Policy from './Policy';
import Rule from './Rule';
export class Detail extends Base {
get name() {
return t('firewalls');
}
get checkEndpoint() {
return true;
}
get endpoint() {
return firewallEndpoint();
}
get tabs() {
const tabs = [
{
title: t('Firewalls'),
key: 'firewalls',
component: Firewall,
},
{
title: t('Firewall Policies'),
key: 'policies',
component: Policy,
},
{
title: t('Firewall Rules'),
key: 'rules',
component: Rule,
},
];
return tabs;
}
}
export default inject('rootStore')(observer(Detail));

View File

@ -45,6 +45,16 @@ import ReverseDetail from '../containers/DNS/Reverse/Detail';
import Zones from '../containers/DNS/Zones';
import ZonesDetail from '../containers/DNS/Zones/Detail';
import RecordSetDetail from '../containers/DNS/Zones/Detail/RecordSets/Detail';
import Firewall from '../containers/Firewall';
import FirewallDetail from '../containers/Firewall/Firewall/Detail';
import FirewallPortDetail from '../containers/Firewall/Firewall/Detail/PortDetail';
import PolicyDetail from '../containers/Firewall/Policy/Detail';
import RuleCreate from '../containers/Firewall/Rule/actions/Create';
import RuleEdit from '../containers/Firewall/Rule/actions/Edit';
import RuleDetail from '../containers/Firewall/Rule/Detail';
import PolicyCreate from '../containers/Firewall/Policy/actions/Create';
import PolicyEdit from '../containers/Firewall/Policy/actions/Edit';
import FirewallCreate from '../containers/Firewall/Firewall/actions/Create';
const PATH = '/network';
export default [
@ -277,6 +287,73 @@ export default [
component: ReverseDetail,
exact: true,
},
{ path: `${PATH}/firewall`, component: Firewall, exact: true },
{
path: `${PATH}/firewall/:firewallId/port/:id`,
component: FirewallPortDetail,
exact: true,
},
{
path: `${PATH}/firewall-admin/:firewallId/port/:id`,
component: FirewallPortDetail,
exact: true,
},
{ path: `${PATH}/firewall-admin`, component: Firewall, exact: true },
{
path: `${PATH}/firewall/detail/:id`,
component: FirewallDetail,
exact: true,
},
{
path: `${PATH}/firewall-admin/detail/:id`,
component: FirewallDetail,
exact: true,
},
{
path: `${PATH}/firewall-policy/detail/:id`,
component: PolicyDetail,
exact: true,
},
{
path: `${PATH}/firewall-policy-admin/detail/:id`,
component: PolicyDetail,
exact: true,
},
{
path: `${PATH}/firewall-rule/create`,
component: RuleCreate,
exact: true,
},
{
path: `${PATH}/firewall-rule/edit/:id`,
component: RuleEdit,
exact: true,
},
{
path: `${PATH}/firewall-rule/detail/:id`,
component: RuleDetail,
exact: true,
},
{
path: `${PATH}/firewall-rule-admin/detail/:id`,
component: RuleDetail,
exact: true,
},
{
path: `${PATH}/firewall/create`,
component: FirewallCreate,
exact: true,
},
{
path: `${PATH}/firewall-policy/add`,
component: PolicyCreate,
exact: true,
},
{
path: `${PATH}/firewall-policy/edit/:id`,
component: PolicyEdit,
exact: true,
},
{ path: '*', component: E404 },
],
},

View File

@ -0,0 +1,65 @@
// 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 globalRootStore from 'stores/root';
import { getDefaultFilter } from './firewall';
export const tableColumns = [
{
title: t('Name'),
dataIndex: 'name',
},
{
title: t('Description'),
dataIndex: 'description',
},
{
title: t('Rules'),
dataIndex: 'rules',
render: (value) => {
if (!value || value.length === 0) {
return '-';
}
return value.map((item) => <div key={item.id}>{item.name}</div>);
},
},
{
title: t('Shared'),
dataIndex: 'shared',
valueRender: 'yesNo',
},
{
title: t('Audited'),
dataIndex: 'audited',
valueRender: 'yesNo',
},
];
export const tableFilter = [
{
label: t('Name'),
name: 'name',
},
getDefaultFilter(t('Hide Default Policies')),
];
export const tableOptions = {
filterParams: tableFilter,
columns: tableColumns,
};
export const isDefault = (item) =>
item.name === 'default egress' || item.name === 'default ingress';
export const isMine = (item) => item.project_id === globalRootStore.projectId;

View File

@ -0,0 +1,80 @@
// 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 { getOptions } from 'utils/index';
import { portStatus } from 'resources/neutron/port';
export const instanceInterfaceOwners = ['compute:nova'];
export const routerInterfaceOwners = [
'network:router_interface',
'network:ha_router_replicated_interface',
'network:router_interface_distributed',
];
export const tableColumns = [
{
title: t('Port'),
dataIndex: 'id',
width: 150,
},
{
title: t('Network'),
dataIndex: 'network_name',
},
{
title: t('Subnet'),
dataIndex: 'subnet_name',
},
{
title: t('IP Address'),
dataIndex: 'ip_address',
},
{
title: t('Owner'),
dataIndex: 'owner',
},
{
title: t('Device'),
dataIndex: 'device_name',
},
{
title: t('Status'),
dataIndex: 'status',
valueMap: portStatus,
},
];
export const tableFilter = [
{
label: t('Network'),
name: 'network',
filterFunc: (record, value) => (record || {}).name.includes(value),
},
{
label: t('Device'),
name: 'router',
filterFunc: (record, value) => (record || {}).name.includes(value),
},
{
label: t('Status'),
name: 'status',
options: getOptions(portStatus),
},
];
export const tableOptions = {
filterParams: tableFilter,
columns: tableColumns,
};

View File

@ -0,0 +1,122 @@
// 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 { getOptions } from 'utils/index';
import { yesNoOptions } from 'utils/constants';
import globalRootStore from 'stores/root';
import { getDefaultFilter } from './firewall';
export const actionInfos = {
allow: t('ALLOW'),
deny: t('DENY'),
reject: t('REJECT'),
};
export const protocolInfos = {
tcp: t('TCP'),
udp: t('UDP'),
icmp: t('ICMP'),
any: t('ANY'),
};
export const tableColumns = [
{
title: t('Name'),
dataIndex: 'name',
},
{
title: t('Description'),
dataIndex: 'description',
isHideable: true,
},
{
title: t('Protocol'),
dataIndex: 'protocol',
valueMap: protocolInfos,
},
{
title: t('Source IP'),
dataIndex: 'source_ip_address',
},
{
title: t('Source Port'),
dataIndex: 'source_port',
},
{
title: t('Destination IP'),
dataIndex: 'destination_ip_address',
},
{
title: t('Destination Port'),
dataIndex: 'destination_port',
},
{
title: t('Rule Action'),
dataIndex: 'action',
valueMap: actionInfos,
},
{
title: t('Enabled'),
dataIndex: 'enabled',
valueRender: 'yesNo',
},
{
title: t('Shared'),
dataIndex: 'shared',
valueRender: 'yesNo',
},
];
export const tableFilter = [
{
label: t('Name'),
name: 'name',
},
{
label: t('Protocol'),
name: 'protocol',
options: getOptions(protocolInfos),
},
{
label: t('Rule Action'),
name: 'action',
options: getOptions(actionInfos),
},
{
label: t('Enabled'),
name: 'enabled',
options: yesNoOptions,
},
{
label: t('Shared'),
name: 'shared',
options: yesNoOptions,
},
getDefaultFilter(t('Hide Default Rules')),
];
export const tableOptions = {
filterParams: tableFilter,
columns: tableColumns,
};
export const isMine = (item) => item.project_id === globalRootStore.projectId;
export const isDefault = (item) =>
[
'default egress ipv4',
'default egress ipv6',
'default ingress ipv4',
'default ingress ipv6',
].includes(item.name);

View File

@ -0,0 +1,70 @@
// 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 globalRootStore from 'stores/root';
import { yesNoOptions } from 'utils/constants';
import { isBoolean } from 'lodash';
export const firewallStatus = {
ACTIVE: t('Active'),
DOWN: t('Down'),
ERROR: t('Error'),
CREATED: t('Created'),
PENDING_CREATE: t('Pending Create'),
PENDING_UPDATE: t('Pending Update'),
PENDING_DELETE: t('Pending Delete'),
INACTIVE: t('Inactive'),
};
export const adminState = {
true: t('Up'),
false: t('Down'),
};
export const transitionStatus = [
'PENDING_CREATE',
// 'PENDING_UPDATE',
'PENDING_DELETE',
];
export const isDefault = (item) => item.name === 'default';
export const isMine = (item) => item.project_id === globalRootStore.projectId;
export const hasNoProject = (item) =>
!item.project_name || item.project_name === '-';
export const getDefaultFilter = (name) => {
const options = yesNoOptions.map((it) => {
if (it.key) {
return {
...it,
checkLabel: name,
isQuick: true,
};
}
return it;
});
return {
label: name,
name: 'notDefault',
options,
filterFunc: (record, value) => {
if (isBoolean(value)) {
return value ? record : true;
}
return value === 'true' ? record : true;
},
};
};

View File

@ -14,6 +14,8 @@
import React from 'react';
import PopoverSubnets from 'components/Popover/PopoverSubnets';
import { isEmpty } from 'lodash';
import globalProjectStore from 'stores/keystone/project';
export const networkStatus = {
ACTIVE: t('Active'),
@ -135,3 +137,88 @@ export const subnetColumns = [
valueRender: 'toLocalTime',
},
];
// deal with quota
export async function fetchNeutronQuota(self) {
self.setState({
quota: {},
quotaLoading: true,
});
const result = await globalProjectStore.fetchProjectNeutronQuota();
self.setState({
quota: result,
quotaLoading: false,
});
}
export const getQuota = (neutronQuota, quotaKeys = ['network']) => {
if (isEmpty(neutronQuota)) {
return {};
}
return quotaKeys.reduce((pre, cur) => {
pre[cur] = neutronQuota[cur] || {};
return pre;
}, {});
};
export const getAdd = (neutronQuota, quotaKeys = ['network'], wishes = [1]) => {
if (isEmpty(neutronQuota)) {
return [];
}
const info = getQuota(neutronQuota, quotaKeys);
let hasError = false;
quotaKeys.forEach((key, index) => {
if (!hasError) {
const quotaDetail = info[key];
const { left = 0 } = quotaDetail || {};
const wish = wishes[index];
if (left !== -1 && left < wish) {
hasError = true;
}
}
});
if (!hasError) {
return wishes;
}
return new Array(quotaKeys.length).fill(0);
};
const titleMap = {
network: t('Network'),
subnet: t('Subnet'),
port: t('Port'),
router: t('Router'),
security_group: t('Security Group'),
security_group_rule: t('Security Group Rule'),
floatingip: t('Floating IP'),
firewall_group: t('Firewall'),
firewall_policy: t('Firewall Policy'),
firewall_rule: t('Firewall Rule'),
};
export const getQuotaInfo = (self, quotaKeys = ['network'], wishes = [1]) => {
const { quota = {}, quotaLoading } = self.state;
if (quotaLoading || isEmpty(quota)) {
return [];
}
const adds = getAdd(quota, quotaKeys, wishes);
const infos = getQuota(quota, quotaKeys);
return quotaKeys.map((key, index) => {
const type = index === 0 ? 'ring' : 'line';
const title = titleMap[key];
const info = infos[key] || {};
return {
...info,
add: adds[index],
name: key,
title,
type,
};
});
};
export const checkQuotaDisable = (quotaKeys, wishes) => {
const { neutronQuota = {} } = globalProjectStore;
const adds = getAdd(neutronQuota, quotaKeys, wishes);
return adds[0] === 0;
};

View File

@ -270,3 +270,86 @@ export const portFilters = [
name: 'name',
},
];
export const getPortColumns = (self) => {
return [
{
title: t('Port'),
dataIndex: 'id',
},
{
title: t('Bind Resource'),
dataIndex: 'server_name',
render: (server_name, item) => {
const { device_id, device_owner } = item;
if (device_id && device_owner === 'compute:nova') {
const value = server_name
? `${device_id} (${server_name})`
: device_id;
const link = self.getLinkRender(
'instanceDetail',
value,
{ id: item.device_id },
{ tab: 'interface' }
);
return (
<>
{item.device_owner}
<br />
{link}
</>
);
}
return (
<>
{item.device_owner}
{item.device_owner && <br />}
{item.device_id || '-'}
</>
);
},
isHideable: true,
sorter: false,
},
{
title: t('Owned Network'),
dataIndex: 'network_id',
routeName: self.getRouteName('networkDetail'),
sorter: false,
render: (value) => {
const link = self.getLinkRender('networkDetail', value, { id: value });
return <>{link}</>;
},
},
{
title: t('Mac Address'),
dataIndex: 'mac_address',
isHideable: true,
},
{
title: t('Status'),
dataIndex: 'status',
width: 80,
valueMap: portStatus,
},
];
};
export const portFilter = [
{
label: t('Network'),
name: 'network_id',
},
{
label: t('Status'),
name: 'status',
options: getOptions(portStatus),
},
];
export const instancePortOptions = (self) => {
return {
columns: getPortColumns(self),
filterParams: portFilter,
};
};

View File

@ -451,6 +451,9 @@ export class ProjectStore extends Base {
floatingip,
security_group,
port,
firewall_group,
firewall_policy,
firewall_rule,
} = data;
const neutronReqBody = {
quota: this.omitNil({
@ -461,6 +464,9 @@ export class ProjectStore extends Base {
security_group,
security_group_rule,
port,
firewall_group,
firewall_policy,
firewall_rule,
}),
};
return neutronReqBody;

View File

@ -0,0 +1,121 @@
// 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 { action } from 'mobx';
import client from 'client';
import { isDefault } from 'resources/neutron/firewall-policy';
import Base from '../base';
export class FirewallPolicyStore extends Base {
get client() {
return client.neutron.firewallPolicies;
}
get listResponseKey() {
return 'firewall_policies';
}
get listFilterByProject() {
return true;
}
get mapper() {
return (data) => ({
...data,
notDefault: !isDefault(data),
});
}
async listDidFetch(items) {
const rules = [];
items.forEach((it) => {
it.firewalls = [];
it.rules = [];
const { firewall_rules } = it;
firewall_rules.forEach((rule) => {
if (rules.indexOf(rule) < 0) {
rules.push(rule);
}
});
});
if (rules.length === 0) {
return items;
}
const ruleResponse = await client.neutron.firewallRules.list();
const allRules = ruleResponse.firewall_rules.map((it) => ({
...it,
protocol: it.protocol || 'any',
}));
items.forEach((it) => {
const { firewall_rules } = it;
it.rules = firewall_rules.map((rule) =>
allRules.find((r) => r.id === rule)
);
});
const firewallResponse = await client.neutron.firewalls.list();
const allFirewalls = firewallResponse.firewall_groups;
items.forEach((it) => {
it.firewalls = allFirewalls.filter(
(firewall) =>
firewall.egress_firewall_policy_id === it.id ||
firewall.ingress_firewall_policy_id === it.id
);
});
return items;
}
async detailDidFetch(item) {
const rules = [];
item.firewalls = [];
item.rules = [];
const { firewall_rules } = item;
firewall_rules.forEach((rule) => {
if (rules.indexOf(rule) < 0) {
rules.push(rule);
}
});
if (rules.length === 0) {
return item;
}
const ruleResponse = await client.neutron.firewallRules.list();
const allRules = ruleResponse.firewall_rules.map((it) => ({
...it,
protocol: it.protocol || 'any',
}));
item.rules = firewall_rules.map((rule) =>
allRules.find((r) => r.id === rule)
);
const firewallResponse = await client.neutron.firewalls.list();
const allFirewalls = firewallResponse.firewall_groups;
item.firewalls = allFirewalls.filter(
(firewall) =>
firewall.egress_firewall_policy_id === item.id ||
firewall.ingress_firewall_policy_id === item.id
);
return item;
}
@action
async insertRule({ id }, body) {
return this.submitting(this.client.insertRule(id, body));
}
@action
async removeRule({ id }, body) {
return this.submitting(this.client.removeRule(id, body));
}
}
const globalFirewallPolicyStore = new FirewallPolicyStore();
export default globalFirewallPolicyStore;

View File

@ -0,0 +1,111 @@
// 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 { get } from 'lodash';
import { action } from 'mobx';
import client from 'client';
import { isDefault } from 'resources/neutron/firewall-rule';
import Base from '../base';
export class FirewallRuleStore extends Base {
get client() {
return client.neutron.firewallRules;
}
get policyClient() {
return client.neutron.firewallPolicies;
}
get listFilterByProject() {
return true;
}
get mapper() {
return (item) => ({
...item,
protocol: item.protocol || 'any',
notDefault: !isDefault(item),
});
}
async listDidFetch(items) {
const policyResponse = await this.policyClient.list();
const allPolicies = policyResponse.firewall_policies;
items.forEach((it) => {
it.policies = allPolicies.filter(
(policy) => policy.firewall_rules.indexOf(it.id) >= 0
);
});
return items;
}
async detailDidFetch(item) {
const policyResponse = await this.policyClient.list();
const allPolicies = policyResponse.firewall_policies;
item.policies = allPolicies.filter(
(policy) => policy.firewall_rules.indexOf(item.id) >= 0
);
return item;
}
@action
async fetchListByPolicy({
limit,
page,
sortKey,
sortOrder,
conditions,
...filters
} = {}) {
this.list.isLoading = true;
// todo: no page, no limit, fetch all
const { tab, all_projects, firewall_rules, ...rest } = filters;
if (firewall_rules.length === 0) {
this.list.isLoading = false;
return;
}
const params = { ...rest };
if (all_projects) {
if (!this.listFilterByProject) {
params.all_projects = true;
}
}
const result = await this.client.list(params);
const allRules = get(result, this.listResponseKey, []);
const data = firewall_rules.map((rule) =>
allRules.find((r) => r.id === rule)
);
const items = data.map(this.mapper);
let newData = await this.listDidFetchProject(items, all_projects);
newData = await this.listDidFetch(newData, all_projects);
this.list.update({
data: newData,
total: items.length || 0,
limit: Number(limit) || 10,
page: Number(page) || 1,
sortKey,
sortOrder,
filters,
isLoading: false,
...(this.list.silent ? {} : { selectedRowKeys: [] }),
});
return newData;
}
}
const globalFirewallRuleStore = new FirewallRuleStore();
export default globalFirewallRuleStore;

View File

@ -0,0 +1,100 @@
// 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 client from 'client';
import { isDefault } from 'resources/neutron/firewall';
import Base from '../base';
import globalProjectMapStore from '../project';
export class FirewallStore extends Base {
get client() {
return client.neutron.firewalls;
}
get policyClient() {
return client.neutron.firewallPolicies;
}
get listFilterByProject() {
return true;
}
get mapper() {
return (data) => ({
...data,
name: data.name || data.id,
notDefault: !isDefault(data),
});
}
async listDidFetch(items) {
const egressPolicies = [];
const ingressPolicies = [];
items.forEach((it) => {
const { egress_firewall_policy_id, ingress_firewall_policy_id } = it;
if (egressPolicies.indexOf(egress_firewall_policy_id) < 0) {
egressPolicies.push(egress_firewall_policy_id);
}
if (ingressPolicies.indexOf(ingress_firewall_policy_id) < 0) {
ingressPolicies.push(ingress_firewall_policy_id);
}
});
if (egressPolicies.length === 0 && ingressPolicies.length === 0) {
return items;
}
const policies = await this.policyClient.list();
items.forEach((it) => {
const { egress_firewall_policy_id, ingress_firewall_policy_id } = it;
it.egressPolicy = policies.firewall_policies.find(
(p) => p.id === egress_firewall_policy_id
);
it.egressPolicyName = it.egressPolicy ? it.egressPolicy.name : '-';
it.ingressPolicy = policies.firewall_policies.find(
(p) => p.id === ingress_firewall_policy_id
);
it.ingressPolicyName = it.ingressPolicy ? it.ingressPolicy.name : '-';
});
return items;
}
async detailDidFetch(item) {
const {
egress_firewall_policy_id,
ingress_firewall_policy_id,
project_id,
} = item;
if (ingress_firewall_policy_id) {
item.ingress = (
await this.fetchPolicy(ingress_firewall_policy_id)
).firewall_policy;
}
if (egress_firewall_policy_id) {
item.egress = (
await this.fetchPolicy(egress_firewall_policy_id)
).firewall_policy;
}
const project = await globalProjectMapStore.fetchProjectDetail({
id: project_id,
});
item.project_name = project ? project.name || '-' : '-';
return item;
}
fetchPolicy(id) {
return this.policyClient.show(id);
}
}
const globalFirewallStore = new FirewallStore();
export default globalFirewallStore;