Setting Up the Overcloud for RHOSP 17.1
Use this procedure to set up the overcloud for a Contrail Networking deployment on RHOSP 17.1.
Download Heat Templates
Upload Container Images to the Undercloud Registry
Use this example procedure on the undercloud to upload overcloud container images to the undercloud registry.
Provision Overcloud Networks
Provision Bare Metal Overcloud Nodes
Configure Contrail
We provide various configuration files in the Contrail Networking Heat Templates package for you to customize your overcloud deployment. Table 2 describes the files that you need to configure in our example.
| File | Description |
|---|---|
| roles_data.yaml | Description of the different node roles |
| contrail-nic-config-Controller.j2 | Description of the interfaces on the OpenStack Controllers |
| contrail-nic-config-ContrailController.j2 | Description of the interfaces on the Contrail Controllers |
| contrail-nic-config-ComputeKernel.j2 | Description of the interfaces on the Compute node running in kernel mode |
| contrail-net.yaml | Description of various Contrail network settings |
| contrail-services.yaml | Description of the Contrail services |
| contrail-plugins.yaml | Miscellaneous settings |
|
Note: You can find samples of the above files under comparable
names in the Contrail Networking Heat Templates package that you downloaded. Our
example, however, assumes that you'll be using the filenames as shown
above.
|
|
- Roles Configuration (roles_data.yaml)
- Network Interface Configuration (*-nic-*.j2)
- Network Parameter Configuration (contrail-net.yaml)
- Contrail Service with Templates (contrail-services.yaml)
- Contrail Plugins (contrail-plugins.yaml)
Roles Configuration (roles_data.yaml)
The roles configuration file contains definitions for the various node roles. You can find sample Contrail roles files in ~/tripleo-heat-templates and in ~/tripleo-heat-templates/roles.
We define three roles in our example. Put all three roles into the roles_data.yaml file and place the file at ~/tripleo-heat-templates/environments/contrail/roles_data.yaml.
OpenStack Controller Role
In our example, we have three nodes for the OpenStack controller. Each of these nodes connects to the Management, Internal API, Management, Storage, Storage Management, and External networks, as shown below:
###############################################################################
# Role: Controller #
###############################################################################
- name: Controller
description: |
Controller role that has all the controler services loaded and handles
Database, Messaging and Network functions.
CountDefault: 3
tags:
- primary
- controller
networks:
Management:
subnet: management_subnet
InternalApi:
subnet: internal_api_subnet
Storage:
subnet: storage_subnet
StorageMgmt:
subnet: storage_mgmt_subnet
External:
subnet: external_subnet
default_route_networks: ['External']
HostnameFormatDefault: '%stackname%-controller-%index%'
RoleParametersDefault:
NeutronPublicInterface: nic2
uses_deprecated_params: True
deprecated_param_extraconfig: 'controllerExtraConfig'
deprecated_param_flavor: 'OvercloudControlFlavor'
deprecated_param_image: 'controllerImage'
deprecated_nic_config_name: 'controller.yaml'
update_serial: 1
ServicesDefault:
<leave unchanged>Contrail Controller Role
We also have three nodes for the Contrail controller. Each of these nodes connects to the Management, Internal API, External, and tenant networks, as shown below:
###############################################################################
# Role: ContrailController #
###############################################################################
- name: ContrailController
description: |
ContrailController role that has all the Contrail controler services loaded
and handles config, control and webui functions
CountDefault: 3
tags:
- primary
- contrailcontroller
networks:
Management:
subnet: management_subnet
InternalApi:
subnet: internal_api_subnet
Tenant:
subnet: tenant_subnet
External:
subnet: external_subnet
HostnameFormatDefault: '%stackname%-contrailcontroller-%index%'
RoleParametersDefault:
NeutronPublicInterface: nic2
tripleo_podman_tls_verify: false
ServicesDefault:
<leave unchanged>
Compute Node Role
We have one Compute node. The compute node connects to the Management, Internal API, Storage, and tenant networks, as shown below.
###############################################################################
# Role: Compute #
###############################################################################
- name: Compute
description: |
Basic Compute Node role
CountDefault: 1
tags:
- compute
networks:
Management:
subnet: management_subnet
InternalApi:
subnet: internal_api_subnet
Storage:
subnet: storage_subnet
Tenant:
subnet: tenant_subnet
HostnameFormatDefault: '%stackname%-novacompute-%index%'
RoleParametersDefault:
NeutronPublicInterface: nic2
FsAioMaxNumber: 1048576
TunedProfileName: "virtual-host"
tripleo_podman_tls_verify: false
uses_deprecated_params: True
deprecated_param_image: 'NovaImage'
deprecated_param_extraconfig: 'NovaComputeExtraConfig'
deprecated_param_metadata: 'NovaComputeServerMetadata'
deprecated_param_scheduler_hints: 'NovaComputeSchedulerHints'
deprecated_param_ips: 'NovaComputeIPs'
deprecated_server_resource_name: 'NovaCompute'
deprecated_nic_config_name: 'compute.yaml'
update_serial: 25
ServicesDefault:
<leave unchanged>Network Interface Configuration (*-nic-*.j2)
NIC configuration files describe the interfaces for each role. You can find sample NIC configuration files in ~/tripleo-heat-templates/network/config/contrail and ~/tripleo-heat-templates/network/config/contrail/examples.
We define three NICs (one for each role) in our example as shown in Table 3.
| Nodes | Interfaces | Networks |
|---|---|---|
| OpenStack Controller | enp1s0 | management |
| enp2s0 | control plane
|
|
| Contrail Controller | enp1s0 | management |
| enp2s0 | control plane
|
|
| enp3s0 | tenant | |
| Compute | enp1s0 | management |
| enp2s0 | control plane
|
|
| enp3s0 | tenant |
- OpenStack Controller NIC (contrail-nic-config-Controller.j2)
- Contrail Controller NIC (contrail-nic-config-ContrailController.j2)
- Compute Node NIC (contrail-nic-config-Compute.j2)
OpenStack Controller NIC (contrail-nic-config-Controller.j2)
---
network_config:
- addresses:
- ip_netmask: {{ management_ip }}/{{ management_subnet_cidr }}
mtu: 1500
name: enp1s0
type: interface
use_dhcp: false
routes:
- default: true
next_hop: {{ management_gateway_ip }}
- addresses:
- ip_netmask: {{ ctlplane_ip }}/{{ ctlplane_subnet_cidr }}
dns_servers: {{ ctlplane_dns_nameservers }}
mtu: 1500
name: enp2s0
type: interface
use_dhcp: false
- addresses:
- ip_netmask: {{ external_ip }}/{{ external_cidr }}
device: enp2s0
mtu: 1500
type: vlan
vlan_id: {{ external_vlan_id }}
- addresses:
- ip_netmask: {{ internal_api_ip }}/{{ internal_api_cidr }}
device: enp2s0
mtu: 1500
type: vlan
vlan_id: {{ internal_api_vlan_id }}
- addresses:
- ip_netmask: {{ storage_mgmt_ip }}/{{ storage_mgmt_cidr }}
device: enp2s0
mtu: 1500
type: vlan
vlan_id: {{ storage_mgmt_vlan_id }}
- addresses:
- ip_netmask: {{ storage_ip }}/{{ storage_cidr }}
device: enp2s0
mtu: 1500
type: vlan
vlan_id: {{ storage_vlan_id }}
Contrail Controller NIC (contrail-nic-config-ContrailController.j2)
---
network_config:
- addresses:
- ip_netmask: {{ management_ip }}/{{ management_subnet_cidr }}
mtu: 1500
name: enp1s0
type: interface
use_dhcp: false
routes:
- default: true
next_hop: {{ management_gateway_ip }}
- addresses:
- ip_netmask: {{ ctlplane_ip }}/{{ ctlplane_subnet_cidr }}
dns_servers: {{ ctlplane_dns_nameservers }}
mtu: 1500
name: enp2s0
type: interface
use_dhcp: false
- addresses:
- ip_netmask: {{ external_ip }}/{{ external_cidr }}
device: enp2s0
mtu: 1500
type: vlan
vlan_id: {{ external_vlan_id }}
- addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
name: enp3s0
mtu: 1500
type: interface
use_dhcp: false
- addresses:
- ip_netmask: {{ internal_api_ip }}/{{ internal_api_cidr }}
device: enp2s0
mtu: 1500
type: vlan
vlan_id: {{ internal_api_vlan_id }}Compute Node NIC (contrail-nic-config-Compute.j2)
---
network_config:
- addresses:
- ip_netmask: {{ management_ip }}/{{ management_subnet_cidr }}
mtu: 1500
name: enp1s0
type: interface
use_dhcp: false
routes:
- default: true
next_hop: {{ management_gateway_ip }}
- addresses:
- ip_netmask: {{ ctlplane_ip }}/{{ ctlplane_subnet_cidr }}
dns_servers: {{ ctlplane_dns_nameservers }}
mtu: 1500
name: enp2s0
type: interface
use_dhcp: false
- addresses:
- ip_netmask: {{ internal_api_ip }}/{{ internal_api_cidr }}
device: enp2s0
mtu: 1500
type: vlan
vlan_id: {{ internal_api_vlan_id }}
- addresses:
- ip_netmask: {{ storage_ip }}/{{ storage_cidr }}
device: enp2s0
mtu: 1500
type: vlan
vlan_id: {{ storage_vlan_id }}
- addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: enp3s0
type: interface
use_dhcp: false
mtu: 1500
name: vhost0
type: contrail_vrouter
use_dhcp: false
Network Parameter Configuration (contrail-net.yaml)
Customize Contrail network parameters by modifying the contrail-net.yaml file. We provide a sample at ~/tripleo-heat-templates/environments/contrail/contrail-net.yaml. Look through that file for explanations of the parameters.
Here is the contrail-net.yaml for our example.
resource_registry:
<leave unchanged>
parameter_defaults:
# Customize all these values to match the local environment
TenantNetCidr: 192.168.33.0/24
InternalApiNetCidr: 192.168.4.0/24
ExternalNetCidr: 10.204.17.0/24
StorageNetCidr: 192.168.2.0/24
StorageMgmtNetCidr: 192.168.3.0/24
ManagementNetCidr: 10.102.70.0/24
# CIDR subnet mask length for provisioning network
ControlPlaneSubnetCidr: '24'
# Allocation pools
ManagementAllocationPools: [{'start': '10.102.70.50', 'end': '10.102.70.99'}]
TenantAllocationPools: [{'start': '192.168.33.50', 'end': '192.168.33.99'}]
InternalApiAllocationPools: [{'start': '192.168.4.50', 'end': '192.168.4.99'}]
ExternalAllocationPools: [{'start': '10.204.17.50', 'end': '10.204.17.99'}]
StorageAllocationPools: [{'start': '192.168.2.50', 'end': '192.168.2.99'}]
StorageMgmtAllocationPools: [{'start': '192.168.3.50', 'end': '192.168.3.99'}]
# Routes
ControlPlaneDefaultRoute: 192.168.213.1
InternalApiDefaultRoute: 192.168.4.1
ExternalInterfaceDefaultRoute: 10.204.17.1
ManagementInterfaceDefaultRoute: 10.102.70.1
# Vlans
InternalApiNetworkVlanID: 710
ExternalNetworkVlanID: 720
StorageNetworkVlanID: 740
StorageMgmtNetworkVlanID: 750
# Services
EC2MetadataIp: 192.168.213.10 # Generally the IP of the Undercloud
DnsServers: ["YOUR_DNS_SERVER"]
NtpServer: "YOUR_NTP_SERVER"
Contrail Service with Templates (contrail-services.yaml)
Customize Contrail services for your network by modifying the contrail-services.yaml file. We provide a sample at ~/tripleo-heat-templates/environments/contrail/contrail-services.yaml. Look through that file for explanations of the parameters.
Here is the contrail-services.yaml for our example.
parameter_defaults:
ServiceNetMap:
<leave unchanged>
NovaLiveMigrationPermitPostCopy: false
NeutronMetadataProxySharedSecret: secret
ContrailRegistry: REGISTRY_FOR_CONTRAIL_CONTAINERS:REGISTRY_PORT
ContrailImageTag: CONTRAIL_TAG
ContrailDefaults:
APPLY_DEFAULTS: "True"
ContrailSettings:
VROUTER_ENCRYPTION: false
VROUTER_GATEWAY: 192.168.33.1
BGP_ASN: 64512
BGP_AUTO_MESH: true
When Contrail is deployed for the first time, the default value of APPLY_DEFAULTS parameter in the ContrailDefaults section needs to be set to 'True'. This enables provisioning parameters present inside the template to use day0 configuration whenever a config provisioning container is restarted. Thus, the provisioning parameters are template driven and any changes to Contrail settings should be done through TripleO templates.
Contrail Networking allows you to configure some global configuration parameters like VXLAN network id mode, linklocal configuration, IBGP auto mesh configuration, enabling 4byte_AS, and changing BGP Global ASN through its web user interface. If you want to manage your cluster through web user interface, then you need to set APPLY_DEFAULTS=False in ContrailDefaults section and deploy your cluster again by running openstack overcloud deploy. This additional step is required because when you have changed Contrail global configuration parameters through web user interface, then there is a possibility for these global configuration parameters to be overwritten if any config provisioner container is restarted. In order to avoid these values to be overwritten, set APPLY_DEFAULTS as 'False' and deploy Contrail again by running openstack overcloud deploy command. As a result, the global configuration parameters remain unchanged as provisioning is not executed again.
For example, if you set APPLY_DEFAULTS=False through TripleO template, deploy your Contrail cluster, set VxLAN Identifier Mode to 'User Configured' from web user interface, and restart config provisioner container, then VxLAN Identifier Mode remains 'User Configured' after the restart of config provisioner container. On the contrary, if APPLY_DEFAULTS is set to True, then after the restart of config provisioner container, VxLAN Identifier Mode will change to its default value, which is Automatic.
For example, if you set APPLY_DEFAULTS=False through TripleO template, deploy your Contrail cluster, set VxLAN Identifier Mode to 'User Configured' from web user interface, and restart config provisioner container, then VxLAN Identifier Mode remains 'User Configured' after the restart of config provisioner container. On the contrary, if APPLY_DEFAULTS is set to True, then after the restart of config provisioner container, VxLAN Identifier Mode changes to its default value, which is Automatic.
APPLY_DEFAULTS=True/False (default: True)
Contrail Plugins (contrail-plugins.yaml)
The Contrail plugins file contains various settings and refers to many other files used by Contrail. It is located at ~/tripleo-heat-templates/environments/contrail/contrail-plugins.yaml.
Don't change this file or change the location of any of the files referenced.
Create the Overcloud
Advanced Configuration
Contrail Networking provides a rich set of capabilities that go beyond our basic example. The following sections contain additional example YAML configuration that might apply to your deployment. This additional configuration is unrelated to our example.
Before using, convert these YAML examples to Jinja2 format. See Red Hat documentation for information on how to do this.
- Advanced vRouter Kernel Mode Configuration
- Advanced vRouter DPDK Mode Configuration
- Advanced vRouter SRIOV + Kernel Mode Configuration
- Advanced vRouter SRIOV + DPDK Mode Configuration
Advanced vRouter Kernel Mode Configuration
In addition to the standard NIC configuration, the vRouter kernel mode supports VLAN, Bond, and Bond + VLAN modes. The configuration snippets below only show the relevant section of the NIC template configuration for each mode.
| Interface Types | Example Configuration |
|---|---|
| VLAN |
- name: enp2s0
type: interface
use_dhcp: false
- type: vlan
device: enp2s0
vlan_id: {{ tenant_vlan_id }}
use_dhcp: false
- name: vhost0
type: contrail_vrouter
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: vlan{{ tenant_vlan_id }}
type: interface
use_dhcp: false
mtu: 1500
use_dhcp: false
|
| Bond |
- name: bond0
type: linux_bond
bonding_options: mode=4 xmit_hash_policy=layer2+3
use_dhcp: false
members:
- type: interface
name: enp2s0
- type: interface
name: enp3s0
- name: vhost0
type: contrail_vrouter
mtu: 1500
use_dhcp: false
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: bond0
type: interface
use_dhcp: false
|
| Bond + VLAN |
- name: bond0
type: linux_bond
bonding_options: mode=4 xmit_hash_policy=layer2+3
use_dhcp: false
members:
- type: interface
name: enp2s0
- type: interface
name: enp3s0
- device: bond0
type: vlan
vlan_id: {{ tenant_vlan_id }}
use_dhcp: false
- name: vhost0
type: contrail_vrouter
mtu: 1500
use_dhcp: false
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: vlan{{ tenant_vlan_id }}
type: interface
use_dhcp: false
|
Advanced vRouter DPDK Mode Configuration
In addition to the standard NIC configuration, the vRouter DPDK mode supports Standard, VLAN, Bond, and Bond + VLAN modes.
Network Environment Configuration:
vi ~/tripleo-heat-templates/environments/contrail/contrail-services.yaml
Enable the number of hugepages:
# For Intel CPU
ContrailDpdkParameters:
KernelArgs: "intel_iommu=on iommu=pt default_hugepagesz=1GB hugepagesz=1G hugepages=4 hugepagesz=2M hugepages=1024"
ExtraSysctlSettings:
# must be equal to value from kernel args: hugepages=4
vm.nr_hugepages:
value: 4
vm.max_map_count:
value: 128960See the following NIC template configurations for vRouter DPDK mode. The configuration snippets below only show the relevant section of the NIC configuration for each mode.
| Interface Types | Example Configuration |
|---|---|
| Standard |
- name: vhost0
type: contrail_vrouter_dpdk
driver: uio_pci_generic
cpu_list: 0x01
mtu: 1500
use_dhcp: false
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: enp2s0
type: interface
use_dhcp: false
|
| VLAN |
- name: vhost0
type: contrail_vrouter_dpdk
driver: uio_pci_generic
cpu_list: 0x01
mtu: 1500
use_dhcp: false
vlan_id: {{ tenant_vlan_id }}
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: enp2s0
type: interface
use_dhcp: false
|
| Bond |
- name: vhost0
type: contrail_vrouter_dpdk
driver: uio_pci_generic
cpu_list: 0x01
mtu: 1500
use_dhcp: false
bond_mode: 4
bond_policy: layer2+3
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: enp2s0
type: interface
use_dhcp: false
- name: enp3s0
type: interface
use_dhcp: false
|
| Bond + VLAN |
- name: vhost0
type: contrail_vrouter_dpdk
driver: uio_pci_generic
cpu_list: 0x01
mtu: 1500
use_dhcp: false
bond_mode: 4
bond_policy: layer2+3
vlan_id: {{ tenant_vlan_id }}
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: enp2s0
type: interface
use_dhcp: false
- name: enp3s0
type: interface
use_dhcp: false
|
Advanced vRouter SRIOV + Kernel Mode Configuration
vRouter SRIOV + Kernel mode can be used in the following combinations:
-
Standard
-
VLAN
-
Bond
-
Bond + VLAN
Network environment configuration:
vi ~/tripleo-heat-templates/environments/contrail/contrail-services.yaml
Enable the number of hugepages:
ContrailSriovParameters:
KernelArgs: "intel_iommu=on iommu=pt default_hugepagesz=1GB hugepagesz=1G hugepages=4 hugepagesz=2M hugepages=1024"
ExtraSysctlSettings:
# must be equal to value from 1G kernel args: hugepages=4
vm.nr_hugepages:
value: 4SRIOV PF/VF settings:
NovaPCIPassthrough: - devname: "ens2f1" physical_network: "sriov1" ContrailSriovNumVFs: ["ens2f1:7"]
The SRIOV NICs are not configured in the NIC templates. However, vRouter NICs must still be configured. See the following NIC template configurations for vRouter kernel mode. The configuration snippets below only show the relevant section of the NIC configuration for each mode.
| Interface Types | Example Configuration |
|---|---|
| VLAN |
- name: ens2f1
type: interface
use_dhcp: false
- type: vlan
device: ens2f1
vlan_id: {{ tenant_vlan_id }}
use_dhcp: false
- name: vhost0
type: contrail_vrouter
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: vlan{{ tenant_vlan_id }}
type: interface
use_dhcp: false
mtu: 1500
use_dhcp: false
|
| Bond |
- name: bond0
type: linux_bond
bonding_options: mode=4 xmit_hash_policy=layer2+3
use_dhcp: false
members:
- type: interface
name: ens2f1
- type: interface
name: ens3f1
- name: vhost0
type: contrail_vrouter
mtu: 1500
use_dhcp: false
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: bond0
type: interface
use_dhcp: false
|
| Bond + VLAN |
- name: bond0
type: linux_bond
bonding_options: mode=4 xmit_hash_policy=layer2+3
use_dhcp: false
members:
- type: interface
name: ens2f1
- type: interface
name: ens3f1
- device: bond0
type: vlan
vlan_id: {{ tenant_vlan_id }}
use_dhcp: false
- name: vhost0
type: contrail_vrouter
mtu: 1500
use_dhcp: false
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: vlan{{ tenant_vlan_id }}
type: interface
use_dhcp: false
|
Advanced vRouter SRIOV + DPDK Mode Configuration
Use vRouter SRIOV + DPDK in the following combinations:
-
Standard
-
VLAN
-
Bond
-
Bond + VLAN
Network environment configuration:
vi ~/tripleo-heat-templates/environments/contrail/contrail-services.yaml
Enable the number of hugepages
ContrailSriovParameters:
KernelArgs: "intel_iommu=on iommu=pt default_hugepagesz=1GB hugepagesz=1G hugepages=4 hugepagesz=2M hugepages=1024"
ExtraSysctlSettings:
# must be equal to value from 1G kernel args: hugepages=4
vm.nr_hugepages:
value: 4SRIOV PF/VF settings
NovaPCIPassthrough:
- devname: "ens2f1"
physical_network: "sriov1"
ContrailSriovNumVFs: ["ens2f1:7"]The SRIOV NICs are not configured in the NIC templates. However, vRouter NICs must still be configured. See the following NIC template configurations for vRouter DPDK mode. The configuration snippets below only show the relevant section of the NIC configuration for each mode.
| Interface Types | Example Configuration |
|---|---|
| Standard |
- name: vhost0
type: contrail_vrouter_dpdk
driver: uio_pci_generic
cpu_list: 0x01
mtu: 1500
use_dhcp: false
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: ens2f1
type: interface
use_dhcp: false
|
| VLAN |
- name: vhost0
type: contrail_vrouter_dpdk
driver: uio_pci_generic
cpu_list: 0x01
mtu: 1500
use_dhcp: false
vlan_id: {{ tenant_vlan_id }}
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: ens2f1
type: interface
use_dhcp: false
|
| Bond |
- name: vhost0
type: contrail_vrouter_dpdk
driver: uio_pci_generic
cpu_list: 0x01
mtu: 1500
use_dhcp: false
bond_mode: 4
bond_policy: layer2+3
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: ens2f1
type: interface
use_dhcp: false
- name: ens2f1
type: interface
use_dhcp: false
|
| Bond + VLAN |
- name: vhost0
type: contrail_vrouter_dpdk
driver: uio_pci_generic
cpu_list: 0x01
mtu: 1500
use_dhcp: false
bond_mode: 4
bond_policy: layer2+3
vlan_id: {{ tenant_vlan_id }}
addresses:
- ip_netmask: {{ tenant_ip }}/{{ tenant_cidr }}
members:
- name: ens2f1
type: interface
use_dhcp: false
- name: ens3f1
type: interface
use_dhcp: false
|