diff --git a/README.md b/README.md index c5b6d812..361866ee 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Hardware or virtual machine with: ## Deployment Scenarios ### Deploy into Internet-Connected Environments -#### [ECS Single-Node All-in-One Deployment (recommended, smallest footprint)](docs/source/installation/ECS-Installation.md) +#### [ECS Single-Node All-in-One Deployment (recommended, fastest, smallest footprint)](docs/source/installation/ECS-Installation.md) Deploy a stand-alone instance of ECS to a single hardware or virtual machine. #### [ECS Multi-Node All-in-One Deployment with Install Node (EC replication with > 3 nodes)](docs/source/installation/ECS-Installation.md) @@ -73,8 +73,8 @@ Please be aware that Install Node bootstrapping requires Internet access to the If you prefer to download a prefab Install Node as an OVF/OVA, follow one of the links below. Please note that OVAs are produced upon each release and do not necessarily have the most current software. -* [dellemc-ecsce-2.0.2-beta.ova](http://130852476153187606.public.ecstestdrive.com/public/dellemc-ecsce-2.0.2-beta.ova) -* [dellemc-ecsce-2.0.2-beta.ova.xz](http://130852476153187606.public.ecstestdrive.com/public/dellemc-ecsce-2.0.2-beta.ova.xz) +* [dellemc-ecsce-2.1.0-vm0.ova](http://130852476153187606.public.ecstestdrive.com/public/dellemc-ecsce-2.1.0-vm0.ova) +* [dellemc-ecsce-2.1.0-vm0.ova.xz](http://130852476153187606.public.ecstestdrive.com/public/dellemc-ecsce-2.1.0-vm0.ova.xz) #### [ECS Single-Node Deployment with Install Node (recommended)](docs/source/installation/ECS-Installation.md) Using an Install Node for isolated environments, deploy a stand-alone instance of ECS to a single hardware or virtual machine. @@ -91,11 +91,11 @@ Using an Install Node for isolated environments, deploy a multi-node ECS instanc | [examples](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/examples) | Deployment and configuration examples for common scenarios | [contrib](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/contrib) | Unsupported community-contributed scripts content related to ECS CE | [patches](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/patches) | Patches to the ECS Community Edition Docker image -| [bootstrap.sh](https://github.com/EMCECS/ECS-CommunityEdition/blob/develop/bootstrap.sh) | Installer 2.0 bootstrap script -| [release.conf](https://github.com/EMCECS/ECS-CommunityEdition/blob/develop/release.conf) | Installer 2.0 release information file -| [ui](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/ui) | Installer 2.0 (`ecsdeploy`, `ecsconfig`, related utilities, and support files) -| [bootstrap_plugins](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/bootstrap_plugins) | Installer 2.0 (`bootstrap.sh` support files) -| [legacy](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/legacy) | Legacy deployment scripts +| [bootstrap.sh](https://github.com/EMCECS/ECS-CommunityEdition/blob/develop/bootstrap.sh) | Install Node bootstrap script +| [release.conf](https://github.com/EMCECS/ECS-CommunityEdition/blob/develop/release.conf) | Installer release information file +| [ui](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/ui) | Install Node utilities and support files +| [bootstrap_plugins](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/bootstrap_plugins) | Install Node bootstrap script support files +| [legacy](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/legacy) | (Legacy) Deployment scripts | [legacy/ecs-single-node](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/legacy/ecs-single-node) | (Legacy) Contains the scripts to run a Elastic Cloud Storage single Node Docker and Vagrant deployments| | [legacy/ecs-multi-node](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/legacy/ecs-multi-node) | (Legacy) Contains the scripts to run a Elastic Cloud Storage Multiple Node Docker deployment| | [legacy/Documentation](https://github.com/EMCECS/ECS-CommunityEdition/tree/develop/legacy/Documentation) | (Legacy) Contains documentation files and media| diff --git a/bootstrap.sh b/bootstrap.sh index 10e57bc3..0ec0f67e 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -47,9 +47,9 @@ cat < Build the installer image (ecs-install) locally instead of fetching - the current release build from DockerHub (not recommended). Use the - Alpine Linux mirror when building the image. + -b Build the installer image (ecs-install) locally using the Alpine Linux + URL instead of pulling the current release build from DockerHub. + WARNING: This is not recommended. [Docker Options] -r Use the Docker registry at instead of DockerHub. @@ -217,12 +217,13 @@ done ############################################################################## ### Main o "" -o " ${release_name} ${release_version} Install Node Bootstrap" +o " ${release_name} Install Node Bootstrap ${ver_maj}.${ver_min}.${ver_rev}${ver_tag}" +o " ${release_product} Image ${release_artifact}:${release_tag}" o "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" - ### No arguments given.. are you sure? if [ -z "$1" ]; then + o "" o "No options were given, but it's possible your environment may" o "depend on one or more options to bootstrap properly." o "" @@ -563,7 +564,7 @@ else v "Tagging ${release_artifact}:${release_tag} -> ${release_common_name}" sudo docker tag "${release_artifact}:${release_tag}" "${release_common_name}" 2>&1 | log fi - +ping_sudo ### Next steps p '' @@ -592,17 +593,16 @@ o '' ### Needs rebooting? if get_os_needs_restarting; then - ping_sudo q "The system has indicated it wants to reboot." o "Please reboot BEFORE continuing to ensure this node is" o "operating with the latest kernel and system libraries." o '' - if $override_flag; then if $override_val; then q "Automatically rebooting by user request (-y argument)" log "REBOOT-REBOOTING-ARGUMENT" + wait_bar 15 "Press CTRL-C to abort" do_reboot else q "Skipping reboot by user request (-n argument)" diff --git a/bootstrap_plugins/centos73.plugin.sh b/bootstrap_plugins/centos73.plugin.sh index a6af70e2..48528ed4 100755 --- a/bootstrap_plugins/centos73.plugin.sh +++ b/bootstrap_plugins/centos73.plugin.sh @@ -25,7 +25,8 @@ do_preflight() { } # packages to install before others -list_prefix_packages='epel-release python-devel wget curl ntp' +#list_prefix_packages='epel-release python-devel wget curl ntp' +list_prefix_packages='wget curl ntp epel-release yum-utils' # script to run for installing prefix_packages in_prefix_packages() { @@ -39,17 +40,19 @@ in_prefix_packages() { } # packages to install -list_general_packages='yum-utils git python-pip python-docker-py' +# list_general_packages='yum-utils git python-pip python-docker-py' +list_general_packages='git python-docker-py' # script to run for installing general_packages in_general_packages() { in_repo_pkg "$list_general_packages" - sudo pip install --upgrade pip - sudo pip install --upgrade virtualenv +# sudo pip install --upgrade pip +# sudo pip install --upgrade virtualenv } # packages to install after others -list_suffix_packages='vim htop iotop iftop jq rsync pigz gdisk aria2' +# list_suffix_packages='vim htop iotop iftop jq rsync pigz gdisk aria2' +list_suffix_packages='htop jq pigz gdisk aria2' # script to run for installing suffix_packages in_suffix_packages() { diff --git a/docs/design/bootstrapper.mmd b/docs/design/bootstrapper.mmd index c5cc5306..de75bf82 100644 --- a/docs/design/bootstrapper.mmd +++ b/docs/design/bootstrapper.mmd @@ -38,6 +38,8 @@ Mind Map generated by NB MindMap plugin ### Alpine mirror +### Base OS Mirror + ## PackageConfigurator ### per\-OS package requirements list diff --git a/docs/design/deploy.yml.mmd b/docs/design/deploy.yml.mmd index 7f631112..769cc3a5 100644 --- a/docs/design/deploy.yml.mmd +++ b/docs/design/deploy.yml.mmd @@ -14,7 +14,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -### install\_node: \ +### install\_node: \ > mmd.emoticon=`anchor` @@ -22,7 +22,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -#### \ | \ +#### \ | \ > mmd.emoticon=`anchor` @@ -32,7 +32,7 @@ Mind Map generated by NB MindMap plugin #### ssh\_port: \ -#### ssh\_username: \ +#### ssh\_username: \ > mmd.emoticon=`anchor` @@ -40,7 +40,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -#### ansible\_user: \ +#### ansible\_user: \ #### ansible\_ssh\_pass: \ @@ -50,7 +50,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -#### dns\_domain: \ +#### dns\_domain: \ > mmd.emoticon=`anchor` @@ -58,7 +58,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -##### \ +##### \ > mmd.emoticon=`anchor` @@ -66,17 +66,17 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -##### \ +##### \ > mmd.emoticon=`anchor` -#### ecs\_root\_user: \ +#### ecs\_root\_user: \ #### ecs\_root\_pass: \ #### entropy\_source: \ -#### autonaming: \ +#### autonaming: \ ### storage\_pool\_defaults: \ @@ -108,7 +108,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -###### \ +###### \ or \ > mmd.emoticon=`anchor` @@ -130,7 +130,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -##### name: \ +##### name: \ > mmd.emoticon=`anchor` @@ -138,7 +138,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -###### \ +###### \ > mmd.emoticon=`anchor` @@ -156,13 +156,13 @@ Mind Map generated by NB MindMap plugin #### full\_replication: \ -#### description: \ +#### description: \ ### replication\_groups: \ #### \: -##### name: \ +##### name: \ > mmd.emoticon=`anchor` @@ -170,7 +170,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -###### \ +###### \ > mmd.emoticon=`anchor` @@ -180,6 +180,22 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` +### management\_user\_defaults: \ + +#### is\_system\_admin: \ + +#### is\_system\_monitor: \ + +### management\_users: \ + +#### username: \ + +#### password: \ | alphanumeric + +#### options: \ + +##### \{k:v any management user option\} + ### namespace\_defaults: \ #### is\_stale\_allowed: \ @@ -190,11 +206,11 @@ Mind Map generated by NB MindMap plugin #### \ -##### name: \ +##### name: \ > mmd.emoticon=`anchor` -##### replication\_group: \ +##### replication\_group: \ > mmd.emoticon=`anchor` @@ -202,7 +218,7 @@ Mind Map generated by NB MindMap plugin > mmd.emoticon=`anchor` -###### \ +###### \ > mmd.emoticon=`anchor` @@ -211,3 +227,25 @@ Mind Map generated by NB MindMap plugin ###### \{k:v any namespace option\} > mmd.emoticon=`anchor` + +### object\_user\_defaults: \ + +#### s3\_expiry\_time: \ + +#### s3\_secret\_key: \ + +#### swift\_password: \ + +#### swift\_groups\_list: \ + +##### \ + +### object\_users: \ + +#### username: \ + +#### namespace: \ + +#### options: \ + +##### \{k:v any object\_user option\} diff --git a/docs/design/deploy.yml.png b/docs/design/deploy.yml.png index 78c643e4..c3d8ef53 100644 Binary files a/docs/design/deploy.yml.png and b/docs/design/deploy.yml.png differ diff --git a/docs/design/reference.deploy.yml b/docs/design/reference.deploy.yml index 742a4b9d..a700e0cd 100644 --- a/docs/design/reference.deploy.yml +++ b/docs/design/reference.deploy.yml @@ -1,4 +1,4 @@ -# deploy.yml reference implementation +# deploy.yml reference implementation v2.1.0 # [Optional] # By changing the license_accepted boolean value to "true" you are @@ -130,7 +130,25 @@ facts: is_full_rep: false # [Optional] - # Namespace defaults. + # Management User defaults + management_user_defaults: + is_system_admin: false + is_system_monitor: false + + # [Optional] + # Management Users + management_users: + - username: admin1 + password: ChangeMe + options: + is_system_admin: true + - username: monitor1 + password: ChangeMe + options: + is_system_monitor: true + + # [Optional] + # Namespace defaults namespace_defaults: is_stale_allowed: false is_compliance_enabled: false @@ -145,3 +163,30 @@ facts: options: is_stale_allowed: false is_compliance_enabled: false + + # [Optional] + # Object User defaults + object_user_defaults: + # Comma-separated list of Swift authorization groups + swift_groups_list: + - users + # Lifetime of S3 secret key in minutes + s3_expiry_time: 2592000 + + # [Optional] + # Object Users + object_users: + - username: object_admin1 + namespace: ns1 + options: + swift_password: ChangeMe + swift_groups_list: + - admin + - users + s3_secret_key: ChangeMeChangeMeChangeMeChangeMeChangeMe + s3_expiry_time: 2592000 + - username: object_user1 + namespace: ns1 + options: + swift_password: ChangeMe + s3_secret_key: ChangeMeChangeMeChangeMeChangeMeChangeMe diff --git a/examples/local-lab-1-node/deploy.yml b/examples/local-lab-1-node/deploy.yml index dbb28834..2940f6bc 100644 --- a/examples/local-lab-1-node/deploy.yml +++ b/examples/local-lab-1-node/deploy.yml @@ -130,7 +130,25 @@ facts: is_full_rep: false # [Optional] - # Namespace defaults. + # Management User defaults + management_user_defaults: + is_system_admin: false + is_system_monitor: false + + # [Optional] + # Management Users + management_users: + - username: admin1 + password: ChangeMe + options: + is_system_admin: true + - username: monitor1 + password: ChangeMe + options: + is_system_monitor: true + + # [Optional] + # Namespace defaults namespace_defaults: is_stale_allowed: false is_compliance_enabled: false @@ -145,3 +163,30 @@ facts: options: is_stale_allowed: false is_compliance_enabled: false + + # [Optional] + # Object User defaults + object_user_defaults: + # Comma-separated list of Swift authorization groups + swift_groups_list: + - users + # Lifetime of S3 secret key in minutes + s3_expiry_time: 2592000 + + # [Optional] + # Object Users + object_users: + - username: object_admin1 + namespace: ns1 + options: + swift_password: ChangeMe + swift_groups_list: + - admin + - users + s3_secret_key: ChangeMeChangeMeChangeMeChangeMeChangeMe + s3_expiry_time: 2592000 + - username: object_user1 + namespace: ns1 + options: + swift_password: ChangeMe + s3_secret_key: ChangeMeChangeMeChangeMeChangeMeChangeMe diff --git a/examples/local-lab-4-node/deploy.yml b/examples/local-lab-4-node/deploy.yml index 9e4f61ee..a6d973a0 100644 --- a/examples/local-lab-4-node/deploy.yml +++ b/examples/local-lab-4-node/deploy.yml @@ -133,7 +133,25 @@ facts: is_full_rep: false # [Optional] - # Namespace defaults. + # Management User defaults + management_user_defaults: + is_system_admin: false + is_system_monitor: false + + # [Optional] + # Management Users + management_users: + - username: admin1 + password: ChangeMe + options: + is_system_admin: true + - username: monitor1 + password: ChangeMe + options: + is_system_monitor: true + + # [Optional] + # Namespace defaults namespace_defaults: is_stale_allowed: false is_compliance_enabled: false @@ -148,3 +166,30 @@ facts: options: is_stale_allowed: false is_compliance_enabled: false + + # [Optional] + # Object User defaults + object_user_defaults: + # Comma-separated list of Swift authorization groups + swift_groups_list: + - users + # Lifetime of S3 secret key in minutes + s3_expiry_time: 2592000 + + # [Optional] + # Object Users + object_users: + - username: object_admin1 + namespace: ns1 + options: + swift_password: ChangeMe + swift_groups_list: + - admin + - users + s3_secret_key: ChangeMeChangeMeChangeMeChangeMeChangeMe + s3_expiry_time: 2592000 + - username: object_user1 + namespace: ns1 + options: + swift_password: ChangeMe + s3_secret_key: ChangeMeChangeMeChangeMeChangeMeChangeMe diff --git a/examples/local-lab-all-in-one/deploy.yml b/examples/local-lab-all-in-one/deploy.yml index d315aada..60ea2752 100644 --- a/examples/local-lab-all-in-one/deploy.yml +++ b/examples/local-lab-all-in-one/deploy.yml @@ -130,7 +130,25 @@ facts: is_full_rep: false # [Optional] - # Namespace defaults. + # Management User defaults + management_user_defaults: + is_system_admin: false + is_system_monitor: false + + # [Optional] + # Management Users + management_users: + - username: admin1 + password: ChangeMe + options: + is_system_admin: true + - username: monitor1 + password: ChangeMe + options: + is_system_monitor: true + + # [Optional] + # Namespace defaults namespace_defaults: is_stale_allowed: false is_compliance_enabled: false @@ -145,3 +163,30 @@ facts: options: is_stale_allowed: false is_compliance_enabled: false + + # [Optional] + # Object User defaults + object_user_defaults: + # Comma-separated list of Swift authorization groups + swift_groups_list: + - users + # Lifetime of S3 secret key in minutes + s3_expiry_time: 2592000 + + # [Optional] + # Object Users + object_users: + - username: object_admin1 + namespace: ns1 + options: + swift_password: ChangeMe + swift_groups_list: + - admin + - users + s3_secret_key: ChangeMeChangeMeChangeMeChangeMeChangeMe + s3_expiry_time: 2592000 + - username: object_user1 + namespace: ns1 + options: + swift_password: ChangeMe + s3_secret_key: ChangeMeChangeMeChangeMeChangeMeChangeMe diff --git a/ui/ansible/clicmd_cache.yml b/ui/ansible/clicmd_cache.yml index ce0beaae..466d46f9 100644 --- a/ui/ansible/clicmd_cache.yml +++ b/ui/ansible/clicmd_cache.yml @@ -1,6 +1,8 @@ - name: Installer | Build the package cache vars: num_data_nodes: "{{ groups['data_node'] | length }}" + top_data_node: "{{ groups['data_node'][0] }}" + install_node: "{{ groups['install_node'][0] }}" hosts: ecs_install roles: - installer_build_cache @@ -8,8 +10,12 @@ - name: Installer | Enable torrent ffx vars: num_data_nodes: "{{ groups['data_node'] | length }}" + top_data_node: "{{ groups['data_node'][0] }}" + install_node: "{{ groups['install_node'][0] }}" hosts: ecs_install tasks: - file: path: "{{ffx_sem}}" state: touch + when: not ( num_data_nodes|int == 1 and top_data_node == install_node ) + diff --git a/ui/ansible/clicmd_deploy.yml b/ui/ansible/clicmd_deploy.yml index c00fb043..83494902 100644 --- a/ui/ansible/clicmd_deploy.yml +++ b/ui/ansible/clicmd_deploy.yml @@ -10,27 +10,46 @@ tasks: - group_by: key=os_{{ ansible_distribution }}_{{ ansible_distribution_major_version }} -- name: CentOS 7 | Install cached packages +- name: CentOS 7 | Synchronize cache hosts: os_CentOS_7 vars: os_tag: os_CentOS_7 + num_data_nodes: "{{ groups['data_node'] | length }}" + top_data_node: "{{ groups['data_node'][0] }}" + install_node: "{{ groups['install_node'][0] }}" roles: - CentOS_7_sync_caches_prep - common_sync_caches_by_torrent + +- name: CentOS 7 | Install cached packages + hosts: os_CentOS_7 + vars: + os_tag: os_CentOS_7 + num_data_nodes: "{{ groups['data_node'] | length }}" + top_data_node: "{{ groups['data_node'][0] }}" + install_node: "{{ groups['install_node'][0] }}" + roles: - CentOS_7_baseline_install - name: Installer | Disable torrent ffx hosts: ecs_install + vars: + num_data_nodes: "{{ groups['data_node'] | length }}" + top_data_node: "{{ groups['data_node'][0] }}" + install_node: "{{ groups['install_node'][0] }}" tasks: - file: path: "{{ffx_sem}}" state: absent + when: not ( num_data_nodes|int == 1 and top_data_node == install_node ) - name: Common | Install ECS gather_facts: True - vars: - num_hosts: "{{ groups['data_node'] | length }}" hosts: data_node + vars: + num_data_nodes: "{{ groups['data_node'] | length }}" + top_data_node: "{{ groups['data_node'][0] }}" + install_node: "{{ groups['install_node'][0] }}" roles: - common_baseline_install - common_deploy diff --git a/ui/ansible/roles/CentOS_7_baseline_install/tasks/main.yml b/ui/ansible/roles/CentOS_7_baseline_install/tasks/main.yml index c2f21ade..14434169 100644 --- a/ui/ansible/roles/CentOS_7_baseline_install/tasks/main.yml +++ b/ui/ansible/roles/CentOS_7_baseline_install/tasks/main.yml @@ -28,6 +28,7 @@ src: "{{host_cache_dir}}/{{os_tag}}/package_cache.tgz" dest: / creates: "{{host_cache_dir}}/disable_package_cache.sem" + when: not ( num_data_nodes|int == 1 and top_data_node == install_node ) # TODO: Improve this # This is suuuuuper hacky - There should be a better way using the yum module, @@ -41,6 +42,7 @@ shell: yum -y -t -e0 -C --nogpgcheck install $(find /var/cache/yum -type f -name "*.rpm") || yum -y -t -e0 -C --nogpgcheck update || true args: creates: "{{host_cache_dir}}/disable_package_cache.sem" + when: not ( num_data_nodes|int == 1 and top_data_node == install_node ) - name: CentOS 7 | Configure ntp template: src=ntp.conf.j2 dest=/etc/ntp.conf diff --git a/ui/ansible/roles/CentOS_7_reboot/tasks/main.yml b/ui/ansible/roles/CentOS_7_reboot/tasks/main.yml index bd8d66c1..8ba88b4d 100644 --- a/ui/ansible/roles/CentOS_7_reboot/tasks/main.yml +++ b/ui/ansible/roles/CentOS_7_reboot/tasks/main.yml @@ -28,9 +28,9 @@ poll: 0 when: "( needs_restarting.stdout | search('[0-9]* : /.*') ) and flag_install_node is not defined" -- name: CentOS 7 | Wait for node to reboot - become: false - local_action: wait_for host="{{ ansible_host | default(inventory_hostname) }}" port=22 state=started delay=60 timeout=300 +#- name: CentOS 7 | Wait for node to reboot actions to take effect +# become: false +# local_action: wait_for host="{{ ansible_host | default(inventory_hostname) }}" port=22 state=started delay=5 timeout=300 # host={{ ansible_default_ipv4.address }} port=22 state=started delay=60 timeout=120 diff --git a/ui/ansible/roles/CentOS_7_sync_caches_prep/tasks/main.yml b/ui/ansible/roles/CentOS_7_sync_caches_prep/tasks/main.yml index 325e2b8d..6f63c600 100644 --- a/ui/ansible/roles/CentOS_7_sync_caches_prep/tasks/main.yml +++ b/ui/ansible/roles/CentOS_7_sync_caches_prep/tasks/main.yml @@ -2,6 +2,7 @@ shell: find /var/cache/yum/ -name 'aria2*.x86_64.rpm' -o -name 'c-ares*.rpm' register: aria_packages delegate_to: "{{ groups['install_node'][0] }}" + when: not ( num_data_nodes|int == 1 and top_data_node == install_node ) - name: CentOS 7 | Make yum cache paths for torrent packages shell: mkdir -p $(dirname {{item}}) diff --git a/ui/ansible/roles/common_baseline_install/tasks/main.yml b/ui/ansible/roles/common_baseline_install/tasks/main.yml index 4780588b..0b29756e 100644 --- a/ui/ansible/roles/common_baseline_install/tasks/main.yml +++ b/ui/ansible/roles/common_baseline_install/tasks/main.yml @@ -71,7 +71,9 @@ args: chdir: "{{host_cache_dir}}/docker" creates: "{{host_cache_dir}}/docker/{{caches.docker.unpack_sem}}" - when: flag_install_node is not defined + when: + - flag_install_node is not defined + - not ( num_data_nodes|int == 1 and top_data_node == install_node ) - name: Common | Update block storage path permissions file: diff --git a/ui/ansible/roles/common_deploy/tasks/main.yml b/ui/ansible/roles/common_deploy/tasks/main.yml index fd987e3a..9b05add2 100644 --- a/ui/ansible/roles/common_deploy/tasks/main.yml +++ b/ui/ansible/roles/common_deploy/tasks/main.yml @@ -82,9 +82,9 @@ # Now we shouldn't have to fiddle with VNeST -- name: debug docker create ecs-storageos - debug: - msg: docker create --name ecs-storageos -e SS_GENCONFIG=1 --volumes-from ecs-config -v /ecs:/dae:z -v /ecs:/disks:z -v /host:/host:z -v /var/log/vipr/emcvipr-object:/var/log:z -v /data:/data:z -v {{host_cache_dir}}/ecs_python:/host/ecs_python:z -v {{entropy_source}}:/dev/random:z --net=host {{product_common_name}} && touch {{host_cache_dir}}/ecs-storageos.created creates={{host_cache_dir}}/ecs-storageos.created +#- name: debug docker create ecs-storageos +# debug: +# msg: docker create --name ecs-storageos -e SS_GENCONFIG=1 --volumes-from ecs-config -v /ecs:/dae:z -v /ecs:/disks:z -v /host:/host:z -v /var/log/vipr/emcvipr-object:/var/log:z -v /data:/data:z -v {{host_cache_dir}}/ecs_python:/host/ecs_python:z -v {{entropy_source}}:/dev/random:z --net=host {{product_common_name}} && touch {{host_cache_dir}}/ecs-storageos.created creates={{host_cache_dir}}/ecs-storageos.created # Don't add a restart policy because we're using init scripts for that. - name: Common | Create the ECS StorageOS container diff --git a/ui/ansible/roles/common_sync_caches_by_torrent/tasks/main.yml b/ui/ansible/roles/common_sync_caches_by_torrent/tasks/main.yml index 85d58c05..c2cc99d5 100644 --- a/ui/ansible/roles/common_sync_caches_by_torrent/tasks/main.yml +++ b/ui/ansible/roles/common_sync_caches_by_torrent/tasks/main.yml @@ -9,7 +9,7 @@ file: state: directory path: "{{host_root_dir}}" - when: "{{flag_install_node is not defined}}" + when: flag_install_node is not defined tags: files - name: Common | Create ecs-install cache directory on nodes @@ -18,7 +18,7 @@ file: state: directory path: "{{host_cache_dir}}" - when: "{{flag_install_node is not defined}}" + when: flag_install_node is not defined tags: files - name: Common | Copy cache torrent digest to nodes @@ -28,7 +28,7 @@ src: "{{cache_dir}}/cache.torrent" dest: "{{host_cache_dir}}/cache.torrent" force: yes - when: "{{flag_install_node is not defined}}" + when: flag_install_node is not defined #creates: "{{host_cache_dir}}/disable_package_cache.sem" - name: Common | Torrent sync caches with nodes @@ -39,4 +39,4 @@ chdir: "{{host_cache_dir}}/.." # creates: "{{host_cache_dir}}/cache_control.sem" creates: "{{host_cache_dir}}/disable_package_cache.sem" - when: "{{flag_install_node is not defined}}" + when: flag_install_node is not defined diff --git a/ui/ansible/roles/installer_build_cache/tasks/main.yml b/ui/ansible/roles/installer_build_cache/tasks/main.yml index fa0c7245..b8fa098a 100644 --- a/ui/ansible/roles/installer_build_cache/tasks/main.yml +++ b/ui/ansible/roles/installer_build_cache/tasks/main.yml @@ -3,6 +3,7 @@ - name: Installer | Create cache directories file: state=directory path={{ cache_dir }}/{{ item }} with_items: "{{ caches.keys() }}" + when: not ( num_data_nodes|int == 1 and top_data_node == install_node ) - name: Installer | Create compressed cache files shell: "{{ item.value.pack_cmd }}" @@ -12,8 +13,10 @@ with_dict: "{{ caches }}" delegate_to: "{{ groups['install_node'][0] }}" register: cacheresults + when: not ( num_data_nodes|int == 1 and top_data_node == install_node ) - name: Installer | Create cache distribution torrent file shell: mktorrent-borg -ig 'facts*' -a udp://{{ groups['install_node'][0] }}:6881 -a http://{{ groups['install_node'][0] }}:6881/announce -o {{ cache_dir }}/cache.torrent -pub {{ cache_dir }} args: creates: "{{ cache_dir }}/cache.torrent" + when: not ( num_data_nodes|int == 1 and top_data_node == install_node ) diff --git a/ui/ansible/testing.yml b/ui/ansible/testing.yml index f4a96587..889bec97 100644 --- a/ui/ansible/testing.yml +++ b/ui/ansible/testing.yml @@ -1,4 +1,7 @@ - name: Installer | Testing - hosts: data_node + vars: + num_data_nodes: "{{ groups['data_node'] | length }}" + top_data_node: "{{ groups['data_node'][0] }}" + install_node: "{{ groups['install_node'][0] }}" roles: - testing diff --git a/ui/ecsconfig.py b/ui/ecsconfig.py index 8b01b39f..461df348 100755 --- a/ui/ecsconfig.py +++ b/ui/ecsconfig.py @@ -261,13 +261,15 @@ def ping(conf, c, w, x): sys.exit(1) except ECSClientException as e: if 'Connection refused' in e.message: - o('FAIL: API service is not alive. This is likely temporary.') + o('WAIT: API service is not alive. This is likely temporary.') elif 'connection failed' in e.message: - o('FAIL: API service is alive but ECS is not. This is likely temporary.') + o('WAIT: API service is alive but ECS is not. This is likely temporary.') elif 'Invalid username or password' in e.message: - o('FAIL: Invalid username or password. If ECS authsvc is bootstrapping, this is likely temporary.') + o('WAIT: Invalid username or password. If ECS authsvc is bootstrapping, this is likely temporary.') elif 'Non-200' in e.message: - o('FAIL: ECS API internal error. If ECS services are still bootstrapping, this is likely temporary.') + o('WAIT: ECS API internal error. If ECS services are still bootstrapping, this is likely temporary.') + elif 'Read timed out' in e.message: + o('WAIT: ECS API timed out. If ECS services are still bootstrapping, this is likely temporary.') else: o('FAIL: Unexpected response from API client: {0}'.format(e)) if not c: @@ -284,6 +286,10 @@ def ping(conf, c, w, x): @click.option('-c', help='Install custom license into ECS from file at given path') @pass_conf def licensing(conf, l, a, c): + """ + Work with ECS Licensing + """ + """ Work with a collection of ECS abstractions :param conf: Click object containing the configuration @@ -299,8 +305,8 @@ def add_license(license_blob): # license_text is a global variable from defaults.py which # can be overridden. # o(license_blob) - license_dict = {"license_text": license_blob.rstrip('\n')} - return conf.api_client.licensing.add_license(license_dict) + #license_dict = {"license_text": license_blob.rstrip('\n')} + return conf.api_client.licensing.add_license(license_blob) def add_default_license(): return add_license(license_text) @@ -892,8 +898,8 @@ def add_all(): o('\t{}'.format(ns_data['name'])) if a: n = None - available_rg_configs = list_all() - if available_rg_configs is not None: + available_ns_configs = list_all() + if available_ns_configs is not None: add_all() o('Created all configured namespaces') else: @@ -906,86 +912,179 @@ def add_all(): o('No namespace named {}'.format(n)) -# @ecsconfig.command('object-user', short_help='Work with ECS Object Users') -# @click.option('-l', is_flag=True, help='List known object user configs') -# @click.option('-r', is_flag=True, help='Get all current object user configs from ECS') -# @click.option('-s', is_flag=True, help='Get current object user configs from ECS for given namespace') -# @click.option('-a', is_flag=True, help="Add all object user to ECS") -# @click.option('-n', default=None, help='Add the given object user to ECS') -# @pass_conf -# def object_user(conf, l, r, s, a, n): -# """ -# Work with a collection of ECS abstractions -# :param conf: Click object containing the configuration -# :param l: list known configurations of this abstraction -# :param r: list instances of this abstraction configured on ECS -# :param a: add all known configurations of this abstraction -# :param n: add a single known configuration of this abstraction -# :return: retval -# """ -# def list_all(): -# pass -# -# def get_all(): -# pass -# -# def get_one(): -# pass -# -# def add_all(): -# pass -# -# def add_one(user_name): -# pass -# -# if l: -# list_all() -# if a: -# n = None -# add_all() -# if n is not None: -# add_one(n) -# -# -# @ecsconfig.command('management-user', short_help='Work with ECS Management Users') -# @click.option('-l', is_flag=True, help='List known management user configs') -# @click.option('-r', is_flag=True, help='Get all current management user configs from ECS') -# @click.option('-s', is_flag=True, help='Get current management user configs from ECS for given namespace') -# @click.option('-a', is_flag=True, help="Add all management user to ECS") -# @click.option('-n', default=None, help='Add the given management user to ECS') -# @pass_conf -# def management_user(conf, l, r, s, a, n): -# """ -# Work with a collection of ECS abstractions -# :param conf: Click object containing the configuration -# :param l: list known configurations of this abstraction -# :param r: list instances of this abstraction configured on ECS -# :param a: add all known configurations of this abstraction -# :param n: add a single known configuration of this abstraction -# :return: retval -# """ -# def list_all(): -# pass -# -# def get_all(): -# pass -# -# def get_one(): -# pass -# -# def add_all(): -# pass -# -# def add_one(user_name): -# pass -# -# if l: -# list_all() -# if a: -# n = None -# add_all() -# if n is not None: -# add_one(n) +@ecsconfig.command('object-user', short_help='Work with ECS Object Users') +@click.option('-l', is_flag=True, help='List known object user configs') +@click.option('-r', is_flag=True, help='Get all current object user configs from ECS') +@click.option('-s', is_flag=False, help='Get current object user configs from ECS for given namespace') +@click.option('-a', is_flag=True, help="Add all object user to ECS") +@click.option('-n', default=None, help='Add the given object user to ECS') +@pass_conf +def object_user(conf, l, r, s, a, n): + """ + Work with a collection of ECS abstractions + :param conf: Click object containing the configuration + :param l: list known configurations of this abstraction + :param r: list instances of this abstraction configured on ECS + :param s: list management users associated with given namespace + :param a: add all known configurations of this abstraction + :param n: add a single known configuration of this abstraction + :return: retval + """ + config_type = 'object user' + + def list_all(): + return conf.ecs.get_ou_names() + + def config_exists(name): + if name in list_all(): + return True + return False + + def get_all(): + list_return = [] + for dict_info in conf.api_client.object_user.list()['blobuser']: + list_return.append(dict_info) + return list_return + + def get_one(name): + return conf.api_client.object_user.get(name) + + def add_all(): + o('Creating all {}s'.format(config_type)) + for this_name in list_all(): + add_one(this_name) + + def add_one(name): + ou_namespace = conf.ecs.get_ou_namespace(name) + ou_dict = conf.ecs.get_ou_dict(name) + + o("Adding {} '{}' to namespace '{}'".format(config_type, name, ou_namespace)) + conf.api_client.object_user.create(name, namespace=ou_namespace) + + o("\tAdding S3 credentials for '{}'".format(name)) + conf.api_client.secret_key.create(user_id=name, + namespace=ou_namespace, + expiry_time=ou_dict['s3_expiry_time'], + secret_key=ou_dict['s3_secret_key']) + + if ou_dict['swift_password'] is not None and ou_dict['swift_groups_list'] is not None: + o("\tAdding Swift credentials for '{}'".format(name)) + conf.api_client.password_group.create(user_id=name, + namespace=ou_namespace, + password=ou_dict['swift_password'], + groups_list=ou_dict['swift_groups_list']) + + available_configs = list_all() + if l: + if available_configs is not None: + o('Available {} configurations:'.format(config_type)) + for name in list_all(): + o('\t{}'.format(name)) + else: + o('No {} configurations in deploy.yml'.format(config_type)) + if a: + n = None + if available_configs is not None: + add_all() + o('Created all configured {}s'.format(config_type)) + else: + o('No {} configurations in deploy.yml'.format(config_type)) + if r: + n = None + items = get_all() + o('All {} configured on ECS:'.format(config_type)) + for item in items: + o("\t" + item) + # if s is not None: + # n = None + # print(get_one(g)) + if n is not None: + if config_exists(n): + add_one(n) + o('Created {} named {}'.format(config_type, n)) + else: + o('No {}s named {}'.format(config_type, n)) + + +@ecsconfig.command('management-user', short_help='Work with ECS Management Users') +@click.option('-l', is_flag=True, help='List known management user configs on Install Node') +@click.option('-r', is_flag=True, help='List current management users on ECS') +@click.option('-g', is_flag=False, help='Get management user info from ECS for given username') +# @click.option('-s', is_flag=False, help='Get current management user configs from ECS for given namespace') +@click.option('-a', is_flag=True, help="Add all management user to ECS") +@click.option('-n', default=None, help='Add the given management user to ECS') +@pass_conf +def management_user(conf, l, r, a, g, n): + """ + Work with a collection of ECS abstractions + :param conf: Click object containing the configuration + :param l: list known configurations of this abstraction + :param r: list instances of this abstraction configured on ECS + :param s: list management users associated with given namespace + :param a: add all known configurations of this abstraction + :param n: add a single known configuration of this abstraction + :return: retval + """ + config_type = 'management user' + + def list_all(): + return conf.ecs.get_mu_names() + + def config_exists(name): + if name in list_all(): + return True + return False + + def get_all(): + list_return = [] + for dict_info in conf.api_client.management_user.list()['mgmt_user_info']: + list_return.append(dict_info['userId']) + return list_return + + def get_one(name): + return conf.api_client.management_user.get(name) + + def add_all(): + for this_name in list_all(): + add_one(this_name) + o('Created {}: {}'.format(config_type, this_name)) + + def add_one(name): + mu_pass = conf.ecs.get_mu_password(name) + mu_dict = conf.ecs.get_mu_dict(name) + conf.api_client.management_user.create(name, password=mu_pass, **mu_dict) + + available_configs = list_all() + + if l: + if available_configs is not None: + o('Available {} configurations:'.format(config_type)) + for name in list_all(): + o('\t{}'.format(name)) + else: + o('No {} configurations in deploy.yml'.format(config_type)) + if a: + n = None + if available_configs is not None: + add_all() + o('Created all configured {}s'.format(config_type)) + else: + o('No {} configurations in deploy.yml'.format(config_type)) + if r: + n = None + items = get_all() + o('All {} configured on ECS:'.format(config_type)) + for item in items: + o("\t" + item) + if g is not None: + n = None + print(get_one(g)) + if n is not None: + if config_exists(n): + add_one(n) + o('Created {} named {}'.format(config_type, n)) + else: + o('No {} named {}'.format(config_type, n)) # # # @ecsconfig.command('bucket', short_help='Work with ECS Buckets') diff --git a/ui/etc/config.yml b/ui/etc/config.yml index 64ccabd1..0544bcef 100644 --- a/ui/etc/config.yml +++ b/ui/etc/config.yml @@ -13,7 +13,7 @@ --- ui: name: Install - version: 2.0 + version: 2.1.0 host_root_dir: /opt/emc/ecs-install state_file: /opt/state.yml deploy_file: /opt/deploy.yml diff --git a/ui/etc/release.conf b/ui/etc/release.conf index 06f043e2..bbc75c31 100644 --- a/ui/etc/release.conf +++ b/ui/etc/release.conf @@ -9,6 +9,7 @@ release_name="ECS Community Edition" release_version="3.0.0.1" +release_product="ECS Software" release_artifact="emccorp/ecs-software-3.0.0" release_tag="latest" release_common_name="emccorp/ecs-software:latest" @@ -30,9 +31,9 @@ repo_name='emccorp' image_name='ecs-install' tag='latest' ver_maj='2' -ver_min='0' -ver_rev='2' -ver_tag='beta' +ver_min='1' +ver_rev='0' +ver_tag='r' serial=0 # TODO: Fill in ecsdeploy_ vars with production values! diff --git a/ui/etc/schema.yml b/ui/etc/schema.yml index dc170b57..2f65fa5a 100644 --- a/ui/etc/schema.yml +++ b/ui/etc/schema.yml @@ -102,45 +102,6 @@ mapping: - type: str func: unix_path - virtual_data_center_defaults: - type: map - required: False - mapping: - description: - type: str - required: False - - replication_group_defaults: - type: map - required: False - mapping: - description: - type: str - required: False - enable_rebalancing: - type: bool - required: False - allow_all_namespaces: - type: bool - required: False - is_full_rep: - type: bool - required: False - - namespace_defaults: - type: map - required: False - mapping: - is_stale_allowed: - required: False - type: bool - is_compliance_enabled: - required: False - type: bool - is_encryption_enabled: - required: False - type: bool - storage_pools: type: seq required: True @@ -177,6 +138,14 @@ mapping: - type: str func: unix_path + virtual_data_center_defaults: + type: map + required: False + mapping: + description: + type: str + required: False + virtual_data_centers: type: seq required: True @@ -201,6 +170,23 @@ mapping: type: str required: False + replication_group_defaults: + type: map + required: False + mapping: + description: + type: str + required: False + enable_rebalancing: + type: bool + required: False + allow_all_namespaces: + type: bool + required: False + is_full_rep: + type: bool + required: False + replication_groups: type: seq required: False @@ -234,6 +220,20 @@ mapping: type: bool required: False + namespace_defaults: + type: map + required: False + mapping: + is_stale_allowed: + required: False + type: bool + is_compliance_enabled: + required: False + type: bool + is_encryption_enabled: + required: False + type: bool + namespaces: type: seq required: False @@ -267,3 +267,98 @@ mapping: is_encryption_enabled: type: bool required: False + + management_user_defaults: + type: map + required: False + mapping: + is_system_admin: + type: bool + required: False + is_system_monitor: + type: bool + required: False + + management_users: + type: seq + required: False + sequence: + - type: map + mapping: + username: + type: str + func: alphanumeric + required: True + password: + type: str + required: True + options: + type: map + required: False + mapping: + is_system_admin: + type: bool + required: False + is_system_monitor: + type: bool + required: False + + object_user_defaults: + type: map + required: False + mapping: + swift_password: + type: str + func: alphanumeric + required: False + swift_groups_list: + type: seq + required: False + sequence: + - type: str + required: True + func: alphanumeric + s3_expiry_time: + type: int + required: False + s3_secret_key: + type: str + func: s3_secret_key + required: False + + object_users: + type: seq + required: False + sequence: + - type: map + mapping: + username: + type: str + func: alphanumeric + required: True + namespace: + type: str + func: alphanumeric + required: True + options: + type: map + required: False + mapping: + swift_password: + type: str + func: alphanumeric + required: False + swift_groups_list: + type: seq + required: False + sequence: + - type: str + required: True + func: alphanumeric + s3_expiry_time: + type: int + required: False + s3_secret_key: + type: str + func: s3_secret_key + required: False diff --git a/ui/libexec/console.sh b/ui/libexec/console.sh index 762d0023..3ff10553 100755 --- a/ui/libexec/console.sh +++ b/ui/libexec/console.sh @@ -73,7 +73,7 @@ error() { # generic error formatter with exit 1 die() { - q "ERROR: ${*}" + error "${*}" # o "If you believe you have received this in error, please open an issue on github." exit 1 } @@ -113,3 +113,15 @@ p() { pw() { printf "\r %${#bar}s\r" } + +wait_bar() { + local _timeout="${1}" + shift + local _timeout_time="$(( $(epoch_now) + _timeout ))" + echo -en "> ${*} " + while [[ "$(epoch_now)" < "${_timeout_time}" ]]; do + echo -n '.' + sleep 1s + done + echo '' +} diff --git a/ui/libexec/osutils.sh b/ui/libexec/osutils.sh index 9f09f2bb..81443015 100755 --- a/ui/libexec/osutils.sh +++ b/ui/libexec/osutils.sh @@ -9,6 +9,20 @@ # limited to the terms and conditions of the License Agreement under which # it is provided by or on behalf of EMC. +# A basic ISO-8601 timestamp with strict capability +timestamp() { + local _char=' ' + if [ "${*}" == "strict" ]; then + _char='T' + fi + printf "%(%Y-%m-%d${_char}%H:%M:%S%z)T\n" -1 +} + +# Get epoch seconds of now +epoch_now() { + printf "%(%s)T\n" -1 +} + # Die if a file doesn't exist (with description) ensure_file_exists() { local filepath="${1}" @@ -103,7 +117,7 @@ is_file_http_accessible() { } ping_sudo() { - sudo -v + retry_with_timeout 5 1800 sudo -v } quit_sudo() { @@ -143,3 +157,19 @@ dump_bootstrap_config() { eval echo "export $_var=\$$_var" done } + +# Simple retry loop with timeout +# retry_with_timeout +retry_with_timeout() { + local _attempts="${1}" + local _timeout="${2}" + shift; shift + local _cmd="${*}" + + local _attempt=0 + local _timeout_time="$(( $(epoch_now) + _timeout))" + + while [[ _attempt < _attempts ]] && [[ "$(epoch_now)" < "${_timeout_time}" ]] && ! $_cmd; do + ((_attempt++)) + done +} diff --git a/ui/resources/docker/Rockerfile b/ui/resources/docker/Rockerfile index f254f2ad..19994c54 100644 --- a/ui/resources/docker/Rockerfile +++ b/ui/resources/docker/Rockerfile @@ -29,8 +29,9 @@ ENV pip_proxy={{ .PipProxy }} RUN apk -q --no-cache upgrade # Add required system packages +#RUN apk -q --no-cache add openssh-client sshpass openssl ca-certificates libffi libressl@edge_main \ RUN apk -q --no-cache add openssh-client sshpass openssl ca-certificates libffi libressl@edge_main \ - make pigz jq less \ + pigz jq less \ opentracker aria2 mktorrent@edge_community \ ansible @@ -44,7 +45,7 @@ RUN mv /etc/profile.d/color_prompt /etc/profile.d/color_prompt.sh \ # Generate temporary build environment and install required Python modules RUN apk -q --no-cache add --virtual .build-deps musl-dev libffi-dev \ - openssl-dev linux-headers git gcc git-perl \ + make openssl-dev linux-headers git gcc git-perl \ && if ! [ -z "$pip_proxy" ]; then \ export pip_proxy="--proxy $pip_proxy" && \ git config --global http.proxy "$http_proxy" \ diff --git a/ui/resources/docker/ecs-install-requirements.txt b/ui/resources/docker/ecs-install-requirements.txt index 2eed1175..8d00eb8a 100644 --- a/ui/resources/docker/ecs-install-requirements.txt +++ b/ui/resources/docker/ecs-install-requirements.txt @@ -1,18 +1,18 @@ click==6.7 -dotmap==1.2.16 -futures==3.0.5 +dotmap==1.2.17 +futures==3.1.1 httplib2==0.10.3 ipaddress==1.0.18 itsdangerous==0.24 -Jinja2==2.9.5 +Jinja2==2.9.6 pager==3.3 paramiko==2.1.2 -ptpython==0.36 +ptpython==0.39 pycrypto==2.6.1 -pyOpenSSL==16.2.0 -python-ecsclient==1.1.0 +pyOpenSSL==17.0.0 +python-ecsclient==1.1.4 PyYAML==3.12 -requests==2.13.0 +requests==2.17.3 sarge==0.1.4 simplejson==3.10.0 six==1.10.0 diff --git a/ui/run.sh b/ui/run.sh index 31d58233..e61d1701 100755 --- a/ui/run.sh +++ b/ui/run.sh @@ -121,7 +121,16 @@ case "$(basename ${0})" in run ecsconfig rg -a || exit $? o "Pinging Management API Endpoint until ready" run ecsconfig ping -c -x || exit $? + run ecsconfig management-user -a || exit $? + o "Pinging Management API Endpoint until ready" + run ecsconfig ping -c -x || exit $? run ecsconfig namespace -a || exit $? + o "Pinging Management API Endpoint until ready" + run ecsconfig ping -c -x || exit $? + run ecsconfig object-user -a || exit $? +# o "Pinging Management API Endpoint until ready" +# run ecsconfig ping -c -x || exit $? +# run ecsconfig bucket -a || exit $? ;; *) die "Invalid operation." diff --git a/ui/setup.py b/ui/setup.py index 5d028a79..27f035c7 100755 --- a/ui/setup.py +++ b/ui/setup.py @@ -3,7 +3,7 @@ setup( name='ecsdeploy', - version='3.0.0.1', + version='2.0.3', packages=find_packages(), scripts=['ui.py', 'ecsdeploy.py', @@ -30,7 +30,8 @@ 'urwid-timed-progress', 'pyopenssl', 'sarge', - 'pykwalify' + 'pykwalify', + 'python-ecsclient' ], entry_points=''' [console_scripts] diff --git a/ui/tui/ecsconf.py b/ui/tui/ecsconf.py index 9c2edb27..5a9bdef8 100644 --- a/ui/tui/ecsconf.py +++ b/ui/tui/ecsconf.py @@ -27,6 +27,8 @@ _D = '_defaults' OPTIONS = 'options' NAME = 'name' +USERNAME = 'username' +PASSWORD = 'password' MEMBERS = 'members' DESC = 'description' DESC_DEFAULT = 'Default description' @@ -126,7 +128,8 @@ NAMESPACE_ADMINS = 'administrators' NAMESPACE_ADMINS_DEFAULT = 'root' NAMESPACE_VPOOL = 'replication_group' -NS = 'namespaces' +NAMESPACE = 'namespace' +NS = NAMESPACE + 's' NS_D = NS[:-1] + _D DEFAULTS[NS] = { 'is_encryption_enabled': False, @@ -138,14 +141,18 @@ MU = 'management_users' MU_D = MU[:-1] + _D DEFAULTS[MU] = { - DESC: DESC_DEFAULT + 'is_system_admin': False, + 'is_system_monitor': False } # Object User stuff OU = 'object_users' OU_D = OU[:-1] + _D DEFAULTS[OU] = { - DESC: DESC_DEFAULT + 's3_expiry_time': 2592000, + 's3_secret_key': None, + 'swift_password': None, + 'swift_groups_list': ['users'] } # Bucket stuff @@ -195,7 +202,14 @@ def get_attr(self, map_type, key=None, name=None): # If map_type, key, and name are provided, then return the name field at key of # map_type in the yaml tree if key is not None and name is not None: - return [x[key] for x in self.deploy.facts[map_type] if x[NAME] == name][0] + try: + attr_map = [x[key] for x in self.deploy.facts[map_type] if x[NAME] == name][0] + except IndexError: + try: + attr_map = [x[key] for x in self.deploy.facts[map_type] if x[USERNAME] == name][0] + except IndexError: + raise + return attr_map except KeyError: return None @@ -235,11 +249,11 @@ def get_fun_facts(self): fun_facts.update(self.get_ansible_facts()) return fun_facts - def get_names(self, map_type): + def get_names(self, map_type, key=NAME): """ Returns a list of name keys from the given map_type """ - return self.get_attr(map_type, NAME) + return self.get_attr(map_type, key) def get_members(self, map_type, name, key=MEMBERS): """ @@ -445,7 +459,7 @@ def get_vdc_options(self, vdc_name): :return: dict of vdc options """ opts = self.get_defaults(VDC) - vdc_opts = self.get_attr(VDC, OPTIONS, vdc_name) + vdc_opts = self.get_attr(VDC, OPTIONS, vdc_name).toDict() if vdc_opts is not None: opts.update(vdc_opts) return opts @@ -492,7 +506,7 @@ def get_ns_options(self, ns_name): :return: dict of rg options """ opts = self.get_defaults(NS) - ns_opts = self.get_attr(NS, OPTIONS, ns_name) + ns_opts = self.get_attr(NS, OPTIONS, ns_name).toDict() if ns_opts is not None: opts.update(ns_opts) return opts @@ -516,3 +530,37 @@ def get_ns_dict(self, ns_name): ns_dict.update({NAMESPACE_ADMINS: self.get_ns_users(ns_name)}) ns_dict.update({NAMESPACE_VPOOL: self.get_ns_vpool(ns_name)}) return ns_dict + + def get_mu_names(self): + return self.get_names(MU, USERNAME) + + def get_mu_options(self, mu_name): + opts = self.get_defaults(MU) + mu_opts = self.get_attr(MU, OPTIONS, mu_name).toDict() + if mu_opts is not None: + opts.update(mu_opts) + return opts + + def get_mu_password(self, mu_name): + return self.get_attr(MU, PASSWORD, mu_name) + + def get_mu_dict(self, mu_name): + mu_dict = {} + mu_dict.update(self.get_mu_options(mu_name)) + return mu_dict + + def get_ou_names(self): + return self.get_names(OU, USERNAME) + + def get_ou_options(self, ou_name): + opts = self.get_defaults(OU) + ou_opts = self.get_attr(OU, OPTIONS, ou_name).toDict() + if ou_opts is not None: + opts.update(ou_opts) + return opts + + def get_ou_namespace(self, ou_name): + return self.get_attr(OU, NAMESPACE, ou_name) + + def get_ou_dict(self, ou_name): + return self.get_ou_options(ou_name) diff --git a/ui/tui/schema_functions.py b/ui/tui/schema_functions.py index a4a11571..b31be043 100644 --- a/ui/tui/schema_functions.py +++ b/ui/tui/schema_functions.py @@ -8,6 +8,7 @@ UnixPathRegex = "^(\/[^\/ ]*)+\/?$" AlphanumericRegex = "^[a-zA-Z0-9]+([_-]?[a-zA-Z0-9])*$" DockerImageRegex = "^(?:(?=[^:\/]{1,253})(?!-)[a-zA-Z0-9-]{1,63}(?