diff --git a/deployment_scripts/puppet/manifests/sscluster-glance.pp b/deployment_scripts/puppet/manifests/sscluster-glance.pp new file mode 100755 index 0000000..417f772 --- /dev/null +++ b/deployment_scripts/puppet/manifests/sscluster-glance.pp @@ -0,0 +1,49 @@ +notice('MODULAR: SwiftStack Swift cluster integration/sscluster-glance.pp') + +$swiftstack = hiera_hash('swiftstack', {}) + +if $swiftstack['metadata']['enabled'] { + + # Plugin options + $swift_as_glance_backend = $swiftstack['swift_as_glance_backend'] + + $glance_hash = hiera_hash('glance', {}) + $management_vip = hiera('management_vip') + $ssl_hash = hiera_hash('use_ssl', {}) + + # Glance options + $glance_user = pick($glance_hash['user'],'glance') + $glance_user_password = $glance_hash['user_password'] + $glance_tenant = pick($glance_hash['tenant'],'services') + $region = hiera('region','RegionOne') + + $internal_auth_protocol = get_ssl_property($ssl_hash, {}, 'keystone', 'internal', 'protocol', 'http') + $internal_auth_address = get_ssl_property($ssl_hash, {}, 'keystone', 'internal', 'hostname', [hiera('service_endpoint', ''), $management_vip]) + + $auth_uri = "${internal_auth_protocol}://${internal_auth_address}:5000/" + + if $swift_as_glance_backend { + notice("Switch Glance backend to Swift Cluster") + + class { 'glance::backend::swift': + swift_store_user => "${glance_tenant}:${glance_user}", + swift_store_key => $glance_user_password, + swift_store_create_container_on_put => 'True', + swift_store_auth_address => "${auth_uri}/v2.0/", + swift_store_region => $region, + } + glance_api_config { + 'glance_store/stores': value => 'glance.store.swift.Store'; + } + Class['glance::backend::swift'] -> + Glance_api_config['glance_store/stores'] ~> + Service['glance-api'] + } + + notice("Start Glance API Service") + service { 'glance-api': + ensure => "running", + hasrestart => true, + hasstatus => true, + } +} diff --git a/deployment_scripts/puppet/manifests/sscluster-keystone.pp b/deployment_scripts/puppet/manifests/sscluster-keystone.pp new file mode 100755 index 0000000..6a9556a --- /dev/null +++ b/deployment_scripts/puppet/manifests/sscluster-keystone.pp @@ -0,0 +1,50 @@ +notice('MODULAR: SwiftStack Swift cluster integration/sscluster-keystone.pp') + +$swiftstack = hiera_hash('swiftstack', {}) + +if $swiftstack['metadata']['enabled'] { + + # Plugin options + $swift_api_address = $swiftstack['swift_api_address'] + $swift_api_fqdn = $swiftstack['swift_api_fqdn'] + $swift_tls_enabled = pick($swiftstack['swift_tls_enabled'], false) + $swift_endpoint_prefix = pick($swiftstack['swift_endpoint_prefix'], 'KEY') + + $swift_hash = hiera_hash('swift', {}) + + # swift_hash options + $password = $swift_hash['user_password'] + $auth_name = pick($swift_hash['auth_name'], 'swift') + $configure_endpoint = pick($swift_hash['configure_endpoint'], true) + $service_name = pick($swift_hash['service_name'], 'swift') + $tenant = pick($swift_hash['tenant'], 'services') + $region = pick($swift_hash['region'], hiera('region', 'RegionOne')) + + $api_address = pick($swift_api_fqdn, $swift_api_address) + + if $swift_tls_enabled { + $swift_protocol = 'https' + $swift_port = pick($swiftstack['port'], 443) + } + else { + $swift_protocol = 'http' + $swift_port = pick($swiftstack['port'], 80) + } + $swift_url = "${swift_protocol}://${api_address}:${swift_port}/v1/${swift_endpoint_prefix}_%(tenant_id)s" + $swift_s3_url = "${swift_protocol}://${api_address}:${swift_port}" + + class { '::swift::keystone::auth': + password => $password, + auth_name => $auth_name, + configure_endpoint => $configure_endpoint, + service_name => $service_name, + public_url => $swift_url, + internal_url => $swift_url, + admin_url => $swift_url, + public_url_s3=> $swift_s3_url, + internal_url_s3 => $swift_s3_url, + admin_url_s3 => $swift_s3_url, + region => $region, + tenant => $tenant, + } +} diff --git a/deployment_scripts/puppet/manifests/sscluster-tls-setup.pp b/deployment_scripts/puppet/manifests/sscluster-tls-setup.pp new file mode 100755 index 0000000..8f32b0c --- /dev/null +++ b/deployment_scripts/puppet/manifests/sscluster-tls-setup.pp @@ -0,0 +1,76 @@ +notice('MODULAR: SwiftStack Swift cluster integration/sscluster-tls-setup.pp') + +$swiftstack = hiera_hash('swiftstack', {}) + +if $swiftstack['metadata']['enabled'] { + + # Plugin options + $swift_api_address = $swiftstack['swift_api_address'] + $swift_api_fqdn = $swiftstack['swift_api_fqdn'] + $swift_tls_enabled = pick($swiftstack['swift_tls_enabled'], false) + $swift_tls_cert = $swiftstack['swift_tls_cert'] + $swift_modify_hosts = $swiftstack['swift_modify_hosts'] + + if $swift_modify_hosts and !empty($swift_api_fqdn) { + host { $swift_api_fqdn: + name => $swift_api_fqdn, + ensure => present, + ip => $swift_api_address, + } + } + + # Add TLS certificate + + File { + owner => 'root', + group => 'root', + mode => '0644', + } + + if $swift_tls_enabled and !empty($swift_tls_cert) { + Exec { + path => '/bin:/usr/bin:/sbin:/usr/sbin', + } + + case $::osfamily { + 'RedHat': { + file { '/etc/pki/ca-trust/source/anchors/swiftstack.pem': + ensure => file, + content => $swift_tls_cert['content'], + notify => Exec['enable_trust'], + } + + exec { 'enable_trust': + command => 'update-ca-trust force-enable', + refreshonly => true, + notify => Exec['add_trust_redhat'], + } + + exec { 'add_trust_redhat': + command => 'update-ca-trust extract', + refreshonly => true, + } + } + + 'Debian': { + file { '/usr/local/share/ca-certificates/swiftstack.crt': + ensure => file, + content => $swift_tls_cert['content'], + notify => Exec['add_trust_debian'], + } + + exec { 'add_trust_debian': + command => 'update-ca-certificates', + refreshonly => true, + } + } + + default: { + fail("Unsupported OS: ${::osfamily}/${::operatingsystem}") + } + } + } + else { + notice("WARNING: you enabled TLS for SwiftStack plugin but did not specified self-signed certificate for adding to OS trust chain. Assuming usage of trusted SwiftStack cert") + } +} diff --git a/deployment_scripts/puppet/manifests/sscluster.pp b/deployment_scripts/puppet/manifests/sscluster.pp deleted file mode 100755 index a74d4df..0000000 --- a/deployment_scripts/puppet/manifests/sscluster.pp +++ /dev/null @@ -1,47 +0,0 @@ - -notice('PLUGIN: SwiftStack Swift cluster integration/sscluster.pp') - -$swiftstack = hiera_hash('swiftstack', {}) -$swift = hiera_hash('swift', {}) -$glance = hiera_hash('glance', {}) - -if $swiftstack['metadata']['enabled'] { - - notice("Enable SwiftStack Swift cluster ingegtation in $deployment_mode") - $role = hiera('roles') - $deployment_mode = hiera('deployment_mode') - $keystone_vip = pick($swift['management_vip'], hiera('management_vip')) - - - $swift_api_address = $swiftstack['swift_api_address'] - $swift_api_fqdn = $swiftstack['swift_api_fqdn'] - $swift_user = pick($swift['user'], 'swift') - $swift_password = $swift['user_password'] - $glance_user = pick($glance['user'],'glance') - $glance_password = $glance['user_password'] - $default_tenant = pick($swift['tenant'], 'services') - - case $swift_api_fqdn { - '': { - $api_address = $swift_api_address - } - default: { - $api_address = $swift_api_fqdn - } - } - - class {'sscluster': - deployment_mode => $deployment_mode, - keystone_vip => $keystone_vip, - api_address => $api_address, - swift_user => $swift_user, - swift_password => $swift_password, - glance_user => $glance_user, - glance_password => $glance_password, - tenant => $default_tenant, - role => $role, - } - -} else { - notice("Disable SwiftStack Swift cluster ingegration") -} diff --git a/deployment_scripts/puppet/modules/sscluster/manifests/init.pp b/deployment_scripts/puppet/modules/sscluster/manifests/init.pp deleted file mode 100755 index bad869e..0000000 --- a/deployment_scripts/puppet/modules/sscluster/manifests/init.pp +++ /dev/null @@ -1,87 +0,0 @@ - -class sscluster ( - $deployment_mode, - $keystone_vip, - $api_address, - $swift_user, - $swift_password, - $glance_user, - $glance_password, - $tenant, - $role = 'controller', -) -{ - $swiftstack = hiera_hash('swiftstack', {}) - $swift_hash = hiera_hash('swift', {}) - - $region = pick($swift_hash['region'], hiera('region', 'RegionOne')) - $ssl_hash = hiera_hash('use_ssl', {}) - - $swift_tls_enabled = pick($swiftstack['swift_tls_enabled']) - case $swift_tls_enabled { - true: { - $ssl_enabled = 'https' - $swift_port = 443 - } - default: { - $ssl_enabled = 'http' - $swift_port = 80 - } - } - - $public_protocol = get_ssl_property($ssl_hash, {}, 'swift', 'public', 'protocol', $ssl_enabled) - $admin_protocol = get_ssl_property($ssl_hash, {}, 'swift', 'admin', 'protocol', $ssl_enabled) - $internal_protocol = get_ssl_property($ssl_hash, {}, 'swift', 'internal', 'protocol', $ssl_enabled) - - $internal_auth_protocol = get_ssl_property($ssl_hash, {}, 'keystone', 'internal', 'protocol', 'http') - $internal_auth_address = get_ssl_property($ssl_hash, {}, 'keystone', 'internal', 'hostname', [hiera('service_endpoint', ''), $keystone_vip]) - $auth_uri = "${internal_auth_protocol}://${internal_auth_address}:5000/v2.0" - - $swift_as_glance_backend = $swiftstack['swift_as_glance_backend'] - if $swift_as_glance_backend { - notice("Switch Glance backend to Swift Cluster: ${api_address}") - class {'glance::backend::swift': - swift_store_user => "$tenant:$glance_user", - swift_store_key => $glance_password, - swift_store_region => $region, - swift_store_auth_address => $auth_uri, - swift_store_create_container_on_put => true, - } - - glance_api_config { - 'glance_store/stores': value => 'glance.store.swift.Store'; - } - } - - if 'primary-controller' in $role { - notice("Update a keystone user for Swift Cluster: ${tenant}:${swift_user}") - class {'swift::keystone::auth': - auth_name => $swift_user, - password => $swift_password, - tenant => $tenant, - region => $region, - port => $swift_port, - public_protocol => $public_protocol, - public_address => $api_address, - admin_protocol => $admin_protocol, - admin_address => $api_address, - internal_protocol => $internal_protocol, - internal_address => $api_address, - endpoint_prefix => 'KEY', - } - Class['swift::keystone::auth'] ~> Service['glance-api'] - } - - notice("Start Glance API Service") - service { 'glance-api': - ensure => "running", - hasrestart => true, - hasstatus => true, - } - - if $swift_as_glance_backend { - Class['glance::backend::swift'] ~> Service['glance-api'] - Glance_api_config<||> ~> Service['glance-api'] - } - -} diff --git a/deployment_tasks.yaml b/deployment_tasks.yaml index f0c0478..ce7889c 100644 --- a/deployment_tasks.yaml +++ b/deployment_tasks.yaml @@ -1,34 +1,66 @@ - id: swift + version: 2.0.0 type: skipped - id: primary-swift + version: 2.0.0 type: skipped - id: openstack-haproxy-swift + version: 2.0.0 type: skipped - id: swift-rebalance-cron + version: 2.0.0 type: skipped - id: swift-keystone + version: 2.0.0 type: skipped - id: swift_zone + version: 2.0.0 type: skipped - id: upload_cirros + version: 2.0.0 type: shell - role: ['primary-controller'] + role: [primary-controller] condition: "settings:swiftstack.upload_cirros_test.value == true" -- id: swiftstack_configure_swift_endpoint - role: ['primary-controller','controller'] - required_for: [post_deployment_end] +- id: swiftstack-tls-setup + version: 2.0.0 + role: [primary-controller, controller, compute, compute-vmware, cinder, cinder-vmware, primary-mongo, mongo, ceph-osd, virt] requires: [post_deployment_start] + required_for: [post_deployment_end] type: puppet parameters: - puppet_manifest: puppet/manifests/sscluster.pp - puppet_modules: "puppet/modules:/etc/puppet/modules/" + puppet_manifest: puppet/manifests/sscluster-tls-setup.pp + puppet_modules: puppet/modules:/etc/puppet/modules/ + timeout: 420 + +- id: swiftstack-configure-swift-endpoint + version: 2.0.0 + role: [primary-controller] + required_for: [post_deployment_end, swiftstack-enable-glance-backend] + requires: [post_deployment_start,swiftstack-tls-setup] + type: puppet + parameters: + puppet_manifest: puppet/manifests/sscluster-keystone.pp + puppet_modules: puppet/modules:/etc/puppet/modules/ + timeout: 420 + +- id: swiftstack-enable-glance-backend + version: 2.0.0 + role: [primary-controller, controller] + required_for: [post_deployment_end] + requires: [post_deployment_start, swiftstack-tls-setup] + cross-depends: + - name: swiftstack-configure-swift-endpoint + type: puppet + parameters: + puppet_manifest: puppet/manifests/sscluster-glance.pp + puppet_modules: puppet/modules:/etc/puppet/modules/ timeout: 420 diff --git a/doc/conf.py b/doc/conf.py index 5161b4f..a32a705 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -55,9 +55,9 @@ author = u'Charles Hsu' # built documents. # # The short X.Y version. -version = u'0.2.0' +version = u'0.3.0' # The full version, including alpha/beta/rc tags. -release = u'0.2-0.2.0-1' +release = u'0.3-0.3.0-1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -123,7 +123,7 @@ todo_include_todos = False # The name for this set of Sphinx documents. # " v documentation" by default. -html_title = 'SwiftStack Plugin for Fuel (v0.2.0)' +html_title = 'SwiftStack Plugin for Fuel (v0.3.0)' # A shorter title for the navigation bar. Default is the same as html_title. html_short_title = 'SwiftStack Plugin for Fuel' diff --git a/doc/images/1_add_nodes.png b/doc/images/1_add_nodes.png index 26b9645..49f202d 100644 Binary files a/doc/images/1_add_nodes.png and b/doc/images/1_add_nodes.png differ diff --git a/doc/images/2_enable_plugin.png b/doc/images/2_enable_plugin.png index aa22a9d..2c6c7a2 100644 Binary files a/doc/images/2_enable_plugin.png and b/doc/images/2_enable_plugin.png differ diff --git a/doc/images/3-1_proxy_outwarding-facing.png b/doc/images/3-1_proxy_outwarding-facing.png index 3e20efa..9b12475 100644 Binary files a/doc/images/3-1_proxy_outwarding-facing.png and b/doc/images/3-1_proxy_outwarding-facing.png differ diff --git a/doc/images/3-2_swift_api_ip.png b/doc/images/3-2_swift_api_ip.png index 7471736..5577f9a 100644 Binary files a/doc/images/3-2_swift_api_ip.png and b/doc/images/3-2_swift_api_ip.png differ diff --git a/doc/images/3_config_network.png b/doc/images/3_config_network.png index 5ef33ce..74263f1 100644 Binary files a/doc/images/3_config_network.png and b/doc/images/3_config_network.png differ diff --git a/doc/images/4_config_interfaces.png b/doc/images/4_config_interfaces.png index 5ef8a52..dd92267 100644 Binary files a/doc/images/4_config_interfaces.png and b/doc/images/4_config_interfaces.png differ diff --git a/doc/images/5_config_key1.png b/doc/images/5_config_key1.png index b4b7c93..18dea13 100644 Binary files a/doc/images/5_config_key1.png and b/doc/images/5_config_key1.png differ diff --git a/doc/images/8_deploy_verify2.png b/doc/images/8_deploy_verify2.png deleted file mode 100644 index 6429ec9..0000000 Binary files a/doc/images/8_deploy_verify2.png and /dev/null differ diff --git a/doc/images/use_on_prem.png b/doc/images/use_on_prem.png index 0fa4633..8629c1e 100644 Binary files a/doc/images/use_on_prem.png and b/doc/images/use_on_prem.png differ diff --git a/doc/images/use_platform.png b/doc/images/use_platform.png index baa6219..2c97e71 100644 Binary files a/doc/images/use_platform.png and b/doc/images/use_platform.png differ diff --git a/doc/index.rst b/doc/index.rst index 34b98b2..3d3b23c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,5 +1,5 @@ -Guide to the Swiftstack plugin for Fuel ver. 0.2-0.2.0-1 +Guide to the Swiftstack plugin for Fuel ver. 0.3.0.3.0-1 ======================================================== .. toctree:: diff --git a/doc/install.rst b/doc/install.rst index 10539ec..ca865b8 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -1,8 +1,6 @@ Installation Guide ================== - - Prepare Fuel Environment ------------------------ #. Prepare a Fuel Master node to install `MOS 8.0`_ @@ -20,13 +18,13 @@ Install Plugin .. code-block:: bash - $ scp swiftstack-0.2-0.2.0-1.noarch.rpm root@:/tmp/ + $ scp swiftstack-0.3.0.3.0-1.noarch.rpm root@:/tmp/ #. Install SwiftStack plugin .. code-block:: bash - [root@fuel ~]$ fuel plugins --install swiftstack-0.2-0.2.0-1.noarch.rpm + [root@fuel ~]$ fuel plugins --install swiftstack-0.3.0.3.0-1.noarch.rpm #. List all Fuel plugins and make sure it’s running @@ -36,7 +34,7 @@ Install Plugin id | name | version | package_version ---|------------|---------|---------------- - 2 | swiftstack | 0.2.0 | 3.0.0 + 2 | swiftstack | 0.3.0 | 4.0.0 diff --git a/doc/overview.rst b/doc/overview.rst index fca1022..875eea3 100644 --- a/doc/overview.rst +++ b/doc/overview.rst @@ -1,7 +1,27 @@ SwiftStack Fuel Plugin ====================== -Allow Mirantis OpenStack environment able to use a running Swift cluster managed by SwiftStack Controller. SwiftStack fuel plugin disables the default Swift cluster that deployed on the Controller or Primary-Controller nodes, and then reconfigures Swift API endpoints, Keystone and Glance settings to the running SwiftStack Swift cluster. +Allow Mirantis OpenStack environment able to use a running Swift cluster managed by +a SwiftStack Controller. In SwiftStack fuel plugin, it disables the default Swift cluster +on Controller and Primary-Controller nodes, and then reconfigures Swift API endpoints, +Keystone, Glance settings and point them to a running SwiftStack Swift cluster. + + +Key terms, acronyms and abbreviations +------------------------------------- + +SwiftStack On-Premises controller + Provides a management service inside user's private place to help users to deploy + and manage Swift clusters. + +SwiftStack Public Controller + Provides a public management service in public cloud that help users to deploy and + manage Swift clusters. + +SwiftStack Nodes + A node installed SwiftStack agents and packages, that can be managed by a + SwiftStack Controller, the node could be assigned a Swift role likes ``Swift node`` + (Proxy/Account/Container/Object services are running in a single node) Requirements ----------- @@ -20,5 +40,24 @@ Requirements Limitations ----------- -The plugin only supports a running SwiftStack Swift, able to reach from the OpenStack environment. -Make sure you have correct network configuration for the Swift cluster and OpenStack environment before you enable this plugin. +The plugin only supports a running SwiftStack Swift cluster and it able to reach +from the OpenStack environment. Make sure you have the correct network configuration +for the Swift cluster and Mirantis OpenStack environment before you enable this plugin. + + + +Known issues +------------ + +#. Need DNS server support to map Swift APIs hostname and IP + + SwiftStack provides a software load balancer, which requries an external DNS server + to operate. Please use DNS server insteand of static hostname records in /etc/hosts. + +#. Self-signed SSL certificates are not supported in the SwiftStack plugin + + Self-signed certificates could be an issue when used in a production environment + because all clients need to trust the cert to pass the TLS/SSL verification. + It is highly recommended to use certificates signed by a known, trusted Certificate + Authority if you require TLS/SSL for your Swift cluster endpoint. + diff --git a/doc/plugin-guide.rst b/doc/plugin-guide.rst new file mode 100644 index 0000000..8a127a3 --- /dev/null +++ b/doc/plugin-guide.rst @@ -0,0 +1,107 @@ +************************************************************ +Guide to the SwiftStack Plugin version 0.3-0.3.0-1 for Fuel +************************************************************ + +This document provides instructions for installing, configuring and using +SwiftStack plugin for Fuel. + +Key terms, acronyms and abbreviations +===================================== + +SwiftStack On-Premises controller + Provides a management service inside user's private place to help users to deploy + and manage Swift clusters. + +SwiftStack Public Controller + Provides a public management service in public cloud that help users to deploy and + manage Swift clusters. + +SwiftStack Nodes + A node installed SwiftStack agents and packages, that can be managed by a + SwiftStack ccontroller, the node could be assigned a Swift role likes ``Swift node`` + (Proxy/Account/Container/Object services are running in a single node) + + +SwiftStack Fuel Plugin +====================== + +Allow Mirantis OpenStack environment able to use a running Swift cluster managed by +a SwiftStack Controller. SwiftStack fuel plugin disables the default Swift cluster +that deployed on the Controller or Primary-Controller nodes, and then reconfigures +Swift API endpoints, Keystone and Glance settings to a running SwiftStack Swift cluster. + +Requirements +------------ + + +License +------- + +========================== ================== +Component License type +========================== ================== +No components are present +========================== ================== + + +Requirements +------------ + ++-----------------------------------+---------------------------------------------+ +|Requirement | Version/Comment | ++===================================+=============================================+ +|Mirantis OpenStack compatibility | 8.0 | ++-----------------------------------+---------------------------------------------+ +|A running SwiftStack Swift cluster | All versions | +| | | +| | Please enable **Keystone Auth** and | +| | **Keystone Auth Token Support** middlewares | ++-----------------------------------+---------------------------------------------+ + +Limitations +----------- + +The plugin only supports a running SwiftStack Swift cluster and it able to reach +from the OpenStack environment. Make sure you have the correct network configuration +for the Swift cluster and Mirantis OpenStack environment before you enable this plugin. + + +Installation Guide +================== + +.. toctree:: + :maxdepth: 2 + + install + + +User Guide +========== + +.. toctree:: + :maxdepth: 2 + + user + +Known issues +============ + +#. Need DNS server support to map Swift APIs hostname and IP + + SwiftStack provides a software load balancer, which requries an external DNS server + to operate. Please use DNS server insteand of static hostname records in /etc/hosts. + +#. Self-signed SSL certificates are not supported in the SwiftStack plugin + + Self-signed certificates could be an issue when used in a production environment + because all clients need to trust the cert to pass the TLS/SSL verification. + It is highly recommended to use certificates signed by a known, trusted Certificate + + +Appendix +======== + +#. SwiftStack docs can be found at https://swiftstack.com/docs/ + + + diff --git a/doc/user.rst b/doc/user.rst index 0845cb8..28369c1 100644 --- a/doc/user.rst +++ b/doc/user.rst @@ -12,15 +12,18 @@ SwiftStack Swift Cluster In SwiftStack Swift cluster, that have three network interfaces need to configure for each node. - #. Outward-facing interface + #. Outward-facing interface: + The clients traffic come into this interface, so if you consider putting an external load balancer in front of the cluster, you should add these outward-facing IPs to the load balancer pool. - #. Cluster-facing interface + #. Cluster-facing interface: + The interface for Swift internal traffic likes proxy-server from/to object-server. - #. Data replication interface + #. Data replication interface: + This interface is dedicated for object replication. If the node only has one network interface, you can assign all network interfaces to this @@ -33,23 +36,31 @@ these three network. Check `Configure network`_ to get more detail. SwiftStack Controller --------------------- -SwiftStack provide two types of controllers, first one is **public controller** (we called `platform controller`) +SwiftStack provide two types of controllers, first one is **public controller** (we called `Platform controller`) and the second one is **On-Premises controller**. The public controller is for customers they don't want to setup a SwiftStack Controller on their data center and also allow the nodes have internet connectivity for management purpose. So, if you don't have an controller account yet, `try to create it`_ . In On-Premises controller, you need to get the setup script and tarball from SwiftStack sales, and they'll help -you to setup an on-premises controller. +you to setup an On-Premises controller. -And make sure you have an account can login to controller and able to setup a swift cluster before you start -to test the plugin, +And make sure you have an account can login to controller and able to setup a Swift cluster before you start +to test the plugin. The network configuration in SwiftStack Controller is quite simple, just check the SwiftStack Nodes can reach SwiftStack controller because SwiftStack Nodes communciate with controller over OpenVPN connections. But if you have a firewall in the middle; please check `SwiftStack Controller Security`_ and `SwiftStack Node Security`_ to configure the firewall. -.. _platform controller: https://platform.swiftstack.com + .. note:: + There is no difference when you use On-Premises or Platform controller to create you own Swift cluster, + and do the integration with SwiftStack Fuel plugin. All configuration of SwiftStack Fuel plugin will + be the same. + + The Swift cluster is outside of Fuel environment, so using the Controller to create and configure your + external Swift cluster which will be more efficienct from scratch. + +.. _Platform controller: https://platform.swiftstack.com .. _try to create it: https://www.swiftstack.com/try-it-now/ .. _SwiftStack Controller Security: https://swiftstack.com/docs/security/controller.html#swiftstack-controller-security @@ -60,13 +71,13 @@ Fuel Slave Nodes ---------------- Fuel slave nodes have three network interfaces to configure, so if SwiftStack Nodes are connected to these -three networks and use same IP range of `fuel's configuration`_, you need to skip the IPs that used for SwiftStack -Nodes. The reason is the fuel master doesn't know which IP is taken from SwiftStack Nodes. +three networks and use same IP range of `Fuel's configuration`_, you need to skip the IPs that used for SwiftStack +Nodes. The reason is the Fuel master doesn't know which IP is taken from SwiftStack Nodes. -The SwiftStack Swift cluster is a standalone cluster, and each client should come from Outward-facing network. -So connected to the fuel slave nodes with Outward-facing network should be enough. +The SwiftStack Swift cluster is a standalone cluster, and each client should come from Outward-facing network +(Fuel Public Network). So connected to the Fuel slave nodes with Outward-facing network should be enough. -.. _fuel's configuration: http://docs.openstack.org/developer/fuel-docs/userdocs/fuel-install-guide/install/install_change_network_interface.html#configure-a-network-interface-for-the-fuel-web-ui +.. _Fuel's configuration: http://docs.openstack.org/developer/fuel-docs/userdocs/fuel-install-guide/install/install_change_network_interface.html#configure-a-network-interface-for-the-fuel-web-ui Network summary --------------- @@ -75,52 +86,100 @@ Please make sure the network configuration like: 1. Fuel controller nodes (Keystone, Glance) can talk to Swift Proxy-server (i.e., Proxy-only, PAC, PACO node) for :ref:`Outward-facing IP`. - 2. Clients can talk to :ref:`Swift API IP Address` (Swift Proxy or External/Internal Load Balancer) - 3. SwiftStack nodes can talk to SwiftStack Controller + 2. Clients can talk to :ref:`Swift API IP Address` (Swift Proxy or + External/Internal Load Balancer) + 3. SwiftStack nodes can talk to SwiftStack Controller via Management (SwiftStack) + network (for On-Premises) or Public network (for public Swiftstack Controller) .. note:: We only use one PACO (Proxy/Account/Comtainer/Object) nodes to deploy a all-in-one Swift cluster in this document and is a minimum deployment. - In real environment, you might setup ten nodes or more for a Swift cluster, - so follow the roles can help you do integration. + In real environment, as the cluster scales, it might be necessary to specalize nodes + into separate Proxy/Account/Container/Object tiers. + +User token validation +--------------------- + +In this document, Swift cluster only connected to ``Fuel Public network``, that means Swift +proxy will veridate user's token through this network, and the admin port ``35357`` is not +available for public url, but use auth port ``5000`` with public url shoud be ok, because +the ``swift`` user has service role in Keystone and it can do token validation in public url. +So the identity and auth url should be same likes ``http[s]://:5000/``. + +If you have another avaialbe interface on SwiftStack nodes can connect to ``Fuel Management network``, +you can switch to admin rul with admin port, so identity url will be ``http[s]://:35357/``. + + .. note:: + You can find the ```` in Fuel environment variables + (deployment_*/primary-controller*.yaml.) Here is the command to help you to find it. + + $ sed -e '/ management:/,/ipaddr:/!d' deployment_*/primary-controller*.yaml | grep ipaddr | awk '{print $2}' +Swift Proxy server validates user's token to keystone through Public network, so the keystone indi + Use SwiftStack On-Premises Controller ------------------------------------- - * 1 Controller Node - * 1 Compute Node (have **Compute** and **Storage - Cinder** roles) - * 1 Swift cluster +Please setup an On-Premises SwiftStack controller first, and then setup a single node Swift +cluster with SwiftStack controller, here is our `quick start guide`_. + * 1 SwiftStack On-Premises controller + * 1 Swift cluster (single node) + +Also prepare two Fuel slave nodes: + + * 1 Controller Node + * 1 Compute Node (have **Compute** and **Storage - Cinder** roles) + .. note:: - In this diagram, the Swift cluster is also connected to management and public network, - for our use case, just make sure the storage network is connected should be enough, - Other interfaces of SwiftStack Nodes, should be used for cluster-facing and replication - networks. + In this diagram, the Swift cluster is also connected to cluster-facing and data replication + network (SwiftStack) outside of Fuel network, which prevents network starvation on Fuel + networks when Swift service daemons are moving data or clients send large data into Swift + cluster. So, just make sure the public network is connected should be enough, Other + interfaces of SwiftStack Nodes, should be used for cluster-facing and replication networks. + Also, the SwiftStack Nodes need to communicate with the SwiftStack controller, so please make + sure they can talked to each other, you can combined ``management (SwiftStack)`` to Fuel + Network as well, that depends on your network design. .. image:: images/use_on_prem.png + Use SwiftStack Public Controller (Platform) ------------------------------------------- +Please setup a single node Swift cluster with our public controller, here is our `quick start guide`_. + + * 1 Swift cluster (single node) + +Also prepare two Fuel slave nodes: + * 1 Controller Node * 1 Compute Node (have **Compute** and **Storage - Cinder** roles) - * 1 Swift cluster + .. note:: - In this diagram, the Swift cluster is also connected to management and public network, - for our use case, just make sure the storage network is connected should be enough, - Other interfaces of SwiftStack Nodes, should be used for cluster-facing and replication - networks. + In this diagram, the Swift cluster is also connected to cluster-facing and data replication + network (SwiftStack) outside of Fuel network, which prevents network starvation on Fuel + networks when Swift service daemons are moving data or clients send large data into Swift + cluster. So, just make sure the storage network is connected should be enough, Other + interfaces of SwiftStack Nodes, should be used for cluster-facing and replication networks. + + + In this case, we use SwiftStack Public controller, so please make sure the SwiftStack + Nodes able to reach Internet. .. image:: images/use_platform.png +.. _quick start guide: https://swiftstack.com/docs/install/index.html + + Deploying Mirantis OpenStack with a SwiftStack Swift cluster ------------------------------------------------------------ @@ -129,8 +188,8 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster * Select **Liberty on Ubuntu Trusty (14.04)** as the distribution * Select **Neutron with VLAN segmentation** as the networking setup * Use all default settings - * 1 Controller Node (has **Controller** and **Storage - Cinder** roles) - * 1 Compute Node (has **Compute** role) + * 1 Controller Node (has **Controller**) + * 1 Compute Node (has **Compute** and **Storage - Cinder** role) .. image:: images/1_add_nodes.png @@ -143,7 +202,7 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster #. **Enable TLS for Swift endpoints**: - This option will use HTTPS for swift endpoints include public, admin and internal urls. + This option will use HTTPS for Swift endpoints include public, admin and internal urls. #. **Swift API IP Address** and **Swift API hostname**: @@ -156,7 +215,7 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster .. note:: If **Use Swift as Glance backend** is disabled, - please consider to enable **Ceph RBD for images (Glance)** or other storage for Glance backend. + please consider enabling **Ceph RBD for images (Glance)** or other storage for Glance backend. If **Enable upload test** is disabled, Fuel won't upload testVM image(cirros-testvm) to Glance and store in Swift cluster. That means some **Functional tests** won't pass: @@ -164,21 +223,28 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster The settings in below, - #. Swift API IP Address: ``192.168.1.100``. + #. Swift API IP Address: ``172.16.0.100``. #. Use Swift as Glance backend: ``Checked`` #. Enable upload test: ``Checked`` .. image:: images/2_enable_plugin.png -#. Go to the **Networks** tab, scroll down to **Storage** section and then - uncheck **Use VLAN tagging** and modify **IP Range** to skip the Swift Proxy IP - (Outwarding-facing) and Swift API IP Address. +#. Go to the **Networks** tab, scroll down to **Public** section and then + modify **IP Range** to skip the Swift Proxy IP (Outwarding-facing) and + Swift API IP Address. .. image:: images/3_config_network.png - If you install SwiftStack node on fuel slave nodes with role ``Operating System``, - please also skip the IPs in Public and Managent IP ranges, because the fuel master - doesn't know which IP addresses used for SwiftStack nodes. + .. note:: + If you install SwiftStack node on Fuel slave nodes with role ``Operating System`` and + please also skip the IPs in Storage and Management IP ranges, because the Fuel master + doesn't know which IP addresses used for SwiftStack nodes. + + In this use case, if you use Storage and Management network for cluster-facing + and data replication network, the Swift cluster interal traffic (Swift proxy to + object server and data replication) will consume more bandwidth on these two networks, + that increase network latency when other OpenStack services are running on the same + networks. .. _proxy_outward_facing_ip: .. _swift_api_ip: @@ -186,7 +252,7 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster .. note:: If you have more than one Proxy server (Proxy-only, PAC, PACO nodes), or you use external/internal load balancer (Swift API IP Address) for - your swift cluster, please consider to skip these IPs. + your Swift cluster, please consider to skip these IPs. * ``Outtward-facing IP from SwiftStack Controller UI`` @@ -205,10 +271,10 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster .. _find_keystone_password: #. Find the settings from deployment information: - * Keystone IP Address (management_vip) + * Keystone IP Address (public_vip) * Swift password - Please login to the fuel master and create a script file called **swiftstack.sh** + Please login to the Fuel master and create a script file called **swiftstack.sh** with contents in below, .. code-block:: bash @@ -223,7 +289,7 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster fuel deployment --env $environment --default # put error checking here - SwiftIP=$(sed -e '/vips:/,/ipaddr:/!d' \ + SwiftIP=$(sed -e '/ public:/,/ipaddr:/!d' \ deployment_*/primary-controller*.yaml \ | grep ipaddr | awk '{print $2}') SwiftPW=$(sed -e '/swift:/,/user_password:/!d' \ @@ -232,7 +298,7 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster echo "Configure Keystone Auth Token Support middleware with the parameters below :" echo "----------------------------------------------------------------------------" - echo " identity URL : http://$SwiftIP:35357/" + echo " identity_url : http://$SwiftIP:5000/" echo " auth_url : http://$SwiftIP:5000/" echo " admin_user : swift" echo " admin_password : $SwiftPW" @@ -253,24 +319,24 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster Default deployment info was downloaded to /root/deployment_5 Configure Keystone Auth Token Support middleware with the parameters below : ---------------------------------------------------------------------------- - identity URL : http://192.168.0.2:35357/ - auth_url : http://192.168.0.2:5000/ + identity_url : http://172.16.0.3:5000/ + auth_url : http://172.16.0.3:5000/ admin_user : swift - admin_password : Ym35Y7j43K6LgsY9xYkJ5TbW + admin_password : v4LiGbh6xPU0vtqXQSMeDjxc .. _setup_swift_middleware: -#. Once we get Keystone IP (192.168.0.2) and Swift user’s password (``Ym35Y7j43K6LgsY9xYkJ5TbW``), \ +#. Once we get Keystone IP (172.16.0.3) and Swift user’s password (``v4LiGbh6xPU0vtqXQSMeDjxc``), \ let’s login to SwiftStack Controller UI to configure Swift cluster * Go to the **Middleware** tab, enable and configure **Keystone Auth Token Support** middleware as below: .. code-block:: bash - identity_url: http://192.168.0.2:35357/ - auth_url: http://192.168.0.2:5000/ + identity_url: http://172.16.0.3:5000/ + auth_url: http://172.16.0.3:5000/ admin_user: swift - admin_password: Ym35Y7j43K6LgsY9xYkJ5TbW + admin_password: v4LiGbh6xPU0vtqXQSMeDjxc admin_tenant_name: services @@ -297,11 +363,25 @@ Deploying Mirantis OpenStack with a SwiftStack Swift cluster Verification ++++++++++++ -Please run the verification steps below to ensure your Swiftstack plugin is configured properly: +Please run the verification steps below to ensure your SwiftStack plugin is configured properly: -#. Check API endpoints from OpenStack Dashboard: +#. Check API endpoints with Keystone CLI: - .. image:: images/8_deploy_verify2.png + .. code-block:: bash + + root@node-23:~# source ~/openrc + + ## + ## Make sure the keystone VIP is correct + ## + root@node-23:~# export OS_AUTH_URL='http://192.168.0.2:5000/v2.0' + + root@node-23:~# keystone endpoint-list |grep KEY + | b858f41ee3704f32a05060932492943b | RegionOne + | http://172.16.0.100:80/v1/KEY_%(tenant_id)s + | http://172.16.0.100:80/v1/KEY_%(tenant_id)s + | http://172.16.0.100:80/v1/KEY_%(tenant_id)s + | 19966ec76f0d455d94caa87d9569a347 | .. _verity_cluster_swift_cli: @@ -316,34 +396,27 @@ Please run the verification steps below to ensure your Swiftstack plugin is conf # Test admin account ~$ cat rc.admin - export ST_AUTH=http://192.168.0.2:5000/v2.0 + export ST_AUTH=http://172.16.0.3:5000/v2.0 export ST_USER=admin:admin export ST_KEY=admin export ST_AUTH_VERSION=2 ~$ source rc.admin ~$ swift stat -v - StorageURL: http://192.168.1.100:80/v1/KEY_c59857e - 9f07a44e691e1a12d3ef71d59 - Auth Token: gAAAAABW77vTlydZxpTB0yiRimVlTorg6IC9GR - lB5moChyd-P6NlsQ_rJva114IecQxxHB4YR5cd - RECCY4VQZnDSP9wgneG-xSi6P4XKwLDmX9lQKb - YGpCb1l19JyiuBdRZyoc3JC0uiFtW6YfQ0mvPp - VOEWgQJ02tL-vBqfFNcuiiWthn20Rok - Account: KEY_c59857e9f07a44e691e1a12d3ef71d59 - Containers: 0 - Objects: 0 - Bytes: 0 - Containers in policy "standard-replica": 0 - Objects in policy "standard-replica": 0 - Bytes in policy "standard-replica": 0 - Accept-Ranges: bytes - X-Account-Project-Domain-Id: default - X-Timestamp: 1458550300.21393 - X-Trans-Id: tx1d579f93ee7846fab0eaa-0056efbbd3 - Content-Type: text/plain; charset=utf-8 - - + StorageURL: http://172.16.0.100:80/v1/KEY_9f12acc2fc1c4b4cb + 75916b2724e2903 + Auth Token: gAAAAABXV5CFn_cx-Y2pJK4de7XDDXvEmfo4SlhmCAAOweG + -RHLkSCCqfc_mGHoJ-7ee4cACSzzx5bXijCtopbRA-Mh2vr + _SGK9GKSB1AIt-Q1kSsUJTNgjL0T6Hws66r7gh4PmiTFwhO + uhV9BTswzF9GzIHdUpKusd3jhrclcc9ipQdnF_bF1c + Account: KEY_9f12acc2fc1c4b4cb75916b2724e2903 + Containers: 0 + Objects: 0 + Bytes: 0 + X-Put-Timestamp: 1465356423.33437 + X-Timestamp: 1465356423.33437 + X-Trans-Id: txf07064e2471544b29f84d-0057579086 + Content-Type: text/plain; charset=utf-8 * Check glance account when **Use Swift as Glance backend** is enabled @@ -355,39 +428,38 @@ Please run the verification steps below to ensure your Swiftstack plugin is conf glance: db_password: XkyxjTF4LKu7FgaY2YyXlUMI image_cache_max_size: '13928339865' - user_password: ZHFGFM7ivEi0XPuL7l4tt5jE + user_password: iqxWViMcHUjxbWD0hqkvjbon # Test glance account ~$ cat rc.glance - export ST_AUTH=http://192.168.0.2:5000/v2.0 + export ST_AUTH=http://172.16.0.3:5000/v2.0 export ST_USER=services:glance - export ST_KEY=ZHFGFM7ivEi0XPuL7l4tt5jE + export ST_KEY=iqxWViMcHUjxbWD0hqkvjbon export ST_AUTH_VERSION=2 ~$ swift stat -v - StorageURL: http://192.168.1.100:80/v1/KEY_fc5bc05137 - 09448da632c525728cf79 - Auth Token: gAAAAABW77t5VpWr7tzqAtOhYhWiQOo11kqeoSS_0 - mnX1WgNprVkAl5Sj8Ut0DuHYnBcg7UdwH00OHfotq - sS9PmetqQSP-RTuQwmwVLH8JAHtpZLm5CFa0ocIJj - o35oFavevzrjsokY4MefxyNlIhByshPelV6Dp3RD0 - C9aBygH96gedpOEUw - Account: KEY_fc5bc0513709448da632c525728cf794 - Containers: 1 - Objects: 1 - Bytes: 13287936 - Containers in policy "standard-replica": 1 - Objects in policy "standard-replica": 1 - Bytes in policy "standard-replica": 13287936 - Accept-Ranges: bytes - X-Account-Project-Domain-Id: default - X-Timestamp: 1458547227.84808 - X-Trans-Id: txac14e38486ea45c98bc6d-0056efbb8d - Content-Type: text/plain; charset=utf-8 - - + StorageURL: http://172.16.0.100:80/v1/KEY_63bda2 + 0adcb24e2eb37d2dcb13d2a29b + Auth Token: gAAAAABXV4-d_FKAboXyxKOoWVdmbiDCLtgX + 0diSqMed9gzXTPHkt5ko7AMffp28iKBX984g + KXqUKk82pjqQ9tpSIu-TA9cTLoZYz0Cabp9Y + s-zIH-BJOP1DZsEaOIOB8wTrvU2i_eGyPKgN + 25iaARIahh2MYUkNU21Xfzg7Q7bQlwvFFhMo + d7g + Account: KEY_63bda20adcb24e2eb37d2dcb13d2a29b + Containers: 1 + Objects: 1 + Bytes: 13287936 + Containers in policy "standard-replica": 1 + Objects in policy "standard-replica": 1 + Bytes in policy "standard-replica": 13287936 + Accept-Ranges: bytes + X-Account-Project-Domain-Id: default + X-Timestamp: 1465322384.96195 + X-Trans-Id: txa59a5b16d6724fc68adb7-0057578f9e + Content-Type: text/plain; charset=utf-8 Appendix diff --git a/environment_config.yaml b/environment_config.yaml index 4050643..515fe7d 100644 --- a/environment_config.yaml +++ b/environment_config.yaml @@ -3,35 +3,41 @@ attributes: toggleable: true group: 'storage' restrictions: - - condition: "settings:storage.objects_ceph.value == true" + - condition: "settings:storage.objects_ceph.value == true" action: disable swift_api_address: value: '192.168.1.100' label: 'Swift API IP Address' description: | - If enabled, + If enabled, * Swift deployment in controller nodes will be disabled * Swift API endpoint in Keysthone DB will redirect to external Swift cluster. - weight: 10 + weight: 5 type: "text" regex: - source: '^((?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3})|(?:^)$' - error: "Invalid IP address" - restrictions: - - condition: "settings:swiftstack.swift_api_fqdn.value != ''" - action: disable - message: "Configures Swift endpoint with hostname" + source: '^((?:\d|1?\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d|1?\d\d|2[0-4]\d|25[0-5])){3})$' + error: "Invalid IP address. NOTE: it cannot be empty" swift_api_fqdn: value: '' label: 'Swift API Hostname' description: "Swift API Hostname" - weight: 15 + weight: 10 type: "text" - + +# swift_modify_hosts: +# weight: 15 +# type: "checkbox" +# value: True +# label: "Add SwiftStack FQDN to /etc/hosts" +# description: | +# Use this option when you need to use domain name for your SwiftStack cluster, +# but it cannot be resolved by DNS server specifed for Fuel environment +# (f.e. in case of self-signed certificates usage). Enabled by default. + swift_tls_enabled: - weight: 5 + weight: 20 type: "checkbox" value: False label: "Enable TLS for Swift endpoints" @@ -42,8 +48,18 @@ attributes: message: "TLS for OpenStack public endpoints is enabled" action: none +# swift_tls_cert: +# value: '' +# label: 'SwiftStack self-signed certificate' +# description: "If you use self-signed certificate for your SwiftStack cluster, please add it here for distribution of Fuel nodes" +# weight: 25 +# type: "file" +# restrictions: +# - condition: "settings:swiftstack.swift_tls_enabled.value == false" +# action: hide + swift_as_glance_backend: - weight: 20 + weight: 30 type: "checkbox" value: false label: "Use Swift as Glance backend" @@ -53,7 +69,7 @@ attributes: action: disable upload_cirros_test: - weight: 30 + weight: 35 type: "checkbox" value: false label: "Enable upload test" @@ -62,4 +78,3 @@ attributes: - condition: "settings:swiftstack.swift_as_glance_backend.value == false" action: disable - diff --git a/metadata.yaml b/metadata.yaml index 45fb437..a5e37b7 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -3,11 +3,12 @@ name: swiftstack # Human-readable name for your plugin title: Enable SwiftStack Swift cluster integration # Plugin version -version: '0.2.0' +version: '0.3.0' # Description description: Integrate Keystone, Glance and an existing SwiftStack Swift cluster # Required fuel version fuel_version: ['8.0'] +is_hotpluggable: true # The plugin is compatible with releases in the list releases: @@ -18,7 +19,7 @@ releases: repository_path: repositories/ubuntu # Version of plugin package -package_version: '3.0.0' +package_version: '4.0.0' # Licences licenses: ['Apache License Version 2.0'] # Specify author or company name diff --git a/pre_build_hook b/pre_build_hook deleted file mode 100755 index 2e530c4..0000000 --- a/pre_build_hook +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -set -eux - -ROOT="$(dirname `readlink -f $0`)" -MODULES="${ROOT}"/deployment_scripts/puppet/modules -RPM_REPO="${ROOT}"/repositories/centos/ -DEB_REPO="${ROOT}"/repositories/ubuntu/ - -# Puppet manifests from fuel-lib -FUEL_LIB_VERSION="7.0" -FUEL_LIB_TARBALL_URL="https://github.com/openstack/fuel-library/archive/${FUEL_LIB_VERSION}.tar.gz" - -# Puppet std library -STDLIB_TARBALL_URL="https://forgeapi.puppetlabs.com/v3/files/puppetlabs-stdlib-4.7.0.tar.gz" -INIFILE_TARBALL_URL="https://forgeapi.puppetlabs.com/v3/files/puppetlabs-inifile-1.4.2.tar.gz" - - -rm -rf "${MODULES:?}"/{openstacklib,glance,swift,keystone,stdlib,inifile} - -# Include dependent manifests from fuel-library -wget -qO- "${FUEL_LIB_TARBALL_URL}" | \ - tar -C "${MODULES}" --strip-components=3 -zxvf - \ - fuel-library-${FUEL_LIB_VERSION}/deployment/puppet/{openstacklib,glance,swift,keystone} - - -mkdir -p "${MODULES}"/{stdlib,inifile} -wget -qO- "${STDLIB_TARBALL_URL}" | tar -C "${MODULES}/stdlib" --strip-components=1 -xz -wget -qO- "${INIFILE_TARBALL_URL}" | tar -C "${MODULES}/inifile" --strip-components=1 -xz - - -