Support Accumulo installs on Microsoft Azure
* Add new cluster type 'azure' which leverages VM Scale Sets
* Increase some CentOS defaults to improve cluster stability
* Add checksums for specific Spark and Hadoop versions, as well as for
Accumulo 2.0.0
diff --git a/README.md b/README.md
index 8a9f9f3..c8f83c2 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
**Muchos automates setting up [Apache Accumulo][accumulo] or [Apache Fluo][fluo] (and their dependencies) on a cluster**
-Muchos makes it easy to launch a cluster in Amazon's EC2 and deploy Accumulo or Fluo to it. Muchos
+Muchos makes it easy to launch a cluster in Amazon's EC2 or Microsoft Azure and deploy Accumulo or Fluo to it. Muchos
enables developers to experiment with Accumulo or Fluo in a realistic, distributed environment.
Muchos installs all software using tarball distributions which makes its easy to experiment
with the latest versions of Accumulo, Hadoop, Zookeeper, etc without waiting for downstream packaging.
@@ -17,35 +17,77 @@
* [Ansible] scripts that install and configure Fluo and its dependencies on a cluster.
* Python scripts that push the Ansible scripts from a local development machine to a cluster and
- run them. These Python scripts can also optionally launch a cluster in EC2 using [boto].
+ run them. These Python scripts can also optionally launch a cluster in EC2 using [boto] or in Azure using Azure CLI.
Checkout [Uno] for setting up Accumulo or Fluo on a single machine.
## Requirements
-Muchos requires the following:
+### Common
-* Python 3
+Muchos requires the following common components for installation and setup:
+
+* Python 3 with a virtual environment setup
+Create a Python 3 environment and switch to it. (We tested using Python 3.6.8,
+but this should work in later versions as well. If you encounter problems,
+please file an issue.)
+```bash
+cd ~
+python3.6 -m venv env
+source env/bin/activate
+```
+* `ssh-agent` installed and running and ssh-agent forwarding. Note that this may
+also require the creation of SSH public-private [key pair](https://help.github.com/en/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent).
+```bash
+eval $(ssh-agent -s)
+ssh-add ~/.ssh/id_rsa
+```
+* Git (current version)
+
+### EC2
+
+Muchos requires the following for EC2 installations:
+
* [awscli] & [boto3] libraries - Install using `pip3 install awscli boto3 --upgrade --user`
-* `ssh-agent` installed and running
* An AWS account with your SSH public key uploaded. When you configure [muchos.props], set `key.name`
to name of your key pair in AWS.
* `~/.aws` [configured][aws-config] on your machine. Can be created manually or using [aws configure][awscli-config].
+### Azure
+
+Muchos requires the following for Azure installations:
+
+* [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) must be installed,
+ configured and authenticated to an Azure subscription. Please note - you should install
+ [Azure CLI 2.0.69](https://packages.microsoft.com/yumrepos/azure-cli/azure-cli-2.0.69-1.el7.x86_64.rpm) on CentOS.
+ Higher versions of Azure CLI are unsupported for Muchos on CentOS at this time until
+ [this issue](https://github.com/Azure/azure-cli/issues/10128) in the Azure CLI 2.0.70 is fixed.
+ Example command to install Azure CLI 2.0.69 on CentOS is below:
+```bash
+wget https://packages.microsoft.com/yumrepos/azure-cli/azure-cli-2.0.69-1.el7.x86_64.rpm
+sudo yum install azure-cli-2.0.69-1.el7.x86_64.rpm
+```
+* An Azure account with permissions to either use an existing or create new Resource Groups, Virtual Networks and Subnets
+* A machine which can connect to securely deploy the cluster in Azure.
+* Install [Ansible for Azure](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ansible-install-configure) within
+ the Python virtual environment by using `pip install ansible[azure]`
+
## Quickstart
-The following commands will install Muchos, launch an EC2 cluster, and setup/run Accumulo:
+The following commands will install Muchos, launch a cluster, and setup/run Accumulo:
```bash
git clone https://github.com/apache/fluo-muchos.git
+
cd fluo-muchos/
cp conf/muchos.props.example conf/muchos.props
vim conf/muchos.props # Edit to configure Muchos cluster
-./bin/muchos launch -c mycluster # Launches Muchos cluster in EC2
+./bin/muchos launch -c mycluster # Launches Muchos cluster in EC2 or Azure
./bin/muchos setup # Set up cluster and start Accumulo
```
-The `setup` command can be run repeatedly to fix any failures and will not repeat successful operations.
+The `launch` command will create a cluster with the name specified in the command (e.g. 'mycluster'). The `setup`
+command can be run repeatedly to fix any failures and will not repeat successful operations.
After your cluster is launched, SSH to it using the following command:
@@ -92,11 +134,47 @@
./bin/muchos status
+## Launching an Azure cluster
+
+Before launching a cluster, you will need to complete the requirements for Azure above, clone the Muchos repo, and
+create [muchos.props] by making a copy of existing [muchos.props.example]. If you want to give others access to your
+cluster, add their public keys to a file named `keys` in your `conf/` directory. During the setup of your cluster,
+this file will be appended on each node to the `~/.ssh/authorized_keys` file for the user set by the
+`cluster.username` property. You will also need to ensure you have authenticated to Azure and set the target
+subscription using the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/manage-azure-subscriptions-azure-cli?view=azure-cli-latest).
+
+Muchos by default uses a CentOS 7 image that is hosted in the Azure marketplace. The Azure Linux Agent is already
+pre-installed on the Azure Marketplace images and is typically available from the distribution's package repository.
+Azure requires that the publishers of the endorsed Linux distributions regularly update their images in the Azure
+Marketplace with the latest patches and security fixes, at a quarterly or faster cadence. Updated images in the Azure
+Marketplace are available automatically to customers as new versions of an image SKU.
+
+Edit the values in the sections within [muchos.props] as below
+Under the `general` section, edit following values as per your configuration
+* `cluster_type = azure`
+* `cluster_user` should be set to the name of the administrative user
+* `proxy_hostname` (optional) is the name of the machine which has access to the cluster VNET
+
+Under the `azure` section, edit following values as per your configuration
+* `resource_group` and provide the same created in your Azure subscription for the cluster deployment
+* `vnet` and provide the same created in your Azure subscription for the cluster deployment
+* `subnet` and provide the same created in your Azure subscription for the cluster deployment
+* `numnodes` can be changed as per the cluster size
+* `vm_sku` can be specified from the available skus in the [selected Azure region](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/cli-ps-findimage)
+
+Within Azure the `nodes` section is auto populated with the hostnames and their default roles.
+
+After following the steps above, run the following command to launch an Azure VMSS cluster called `mycluster`
+(where 'mycluster' is the name assigned to your cluster):
+```bash
+.bin/muchos launch -c `mycluster` # Launches Muchos cluster in Azure
+```
+
## Set up the cluster
-The `./bin/muchos setup` command will set up your cluster and start Hadoop, Zookeeper, & Accumulo. It
-will download release tarballs of Fluo, Accumulo, Hadoop, etc. The versions of these tarballs are
-specified in [muchos.props] and can be changed if desired.
+Once your cluster is built in EC2 or Azure, the `./bin/muchos setup` command will set up your cluster and
+start Hadoop, Zookeeper & Accumulo. It will download release tarballs of Fluo, Accumulo, Hadoop, etc. The
+versions of these tarballs are specified in [muchos.props] and can be changed if desired.
Optionally, Muchos can setup the cluster using an Accumulo or Fluo tarball that is placed in the
`conf/upload` directory of Muchos. This option is only necessary if you want to use an unreleased
@@ -199,16 +277,19 @@
useful in user playbooks. It is recommended that any user-defined Ansible playbooks should be
managed in their own git repository (see [mikewalch/muchos-custom][mc] for an example).
-## Terminating your EC2 cluster
+## Terminating your cluster
-If you launched your cluster on EC2, run the following command terminate your cluster. WARNING - All
+If you launched your cluster, run the following command to terminate your cluster. WARNING - All
data on your cluster will be lost:
./bin/muchos terminate
-## Automatic shutdown of EC2 clusters
+Note: The terminate command is currently unsupported for Azure based clusters. Instead, you should delete
+underlying Azure VMSS resources when you need to terminate the cluster.
-With the default configuration, EC2 clusters will not shutdown automatically after a delay and the default
+## Automatic shutdown of clusters
+
+With the default configuration, clusters will not shutdown automatically after a delay and the default
shutdown behavior will be stopping the node. If you would like your cluster to terminate after 8 hours,
set the following configuration in [muchos.props]:
@@ -233,8 +314,10 @@
Muchos is powered by the following projects:
* [boto] - Python library used by `muchos launch` to start a cluster in AWS EC2.
- * [Ansible] - Cluster management tool that is used by `muchos setup` to install, configure, and
+ * [ansible] - Cluster management tool that is used by `muchos setup` to install, configure, and
start Fluo, Accumulo, Hadoop, etc on an existing EC2 or bare metal cluster.
+ * [azure-cli] - The Azure CLI is a command-line tool for managing Azure resources.
+ * [ansible-azure] - Ansible includes a suite of modules for interacting with Azure Resource Manager.
## Muchos Testing
@@ -250,6 +333,8 @@
[aws-config]: http://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html
[awscli]: https://docs.aws.amazon.com/cli/latest/userguide/installing.html
[awscli-config]: http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-quick-configuration
+[azure-cli]: https://packages.microsoft.com/yumrepos/azure-cli/azure-cli-2.0.69-1.el7.x86_64.rpm
+[ansible-azure]: https://docs.ansible.com/ansible/latest/scenario_guides/guide_azure.html
[fluo-app]: https://github.com/apache/fluo/blob/master/docs/applications.md
[WebIndex]: https://github.com/apache/fluo-examples/tree/master/webindex
[Phrasecount]: https://github.com/apache/fluo-examples/tree/master/phrasecount
diff --git a/ansible/accumulo.yml b/ansible/accumulo.yml
index dca90eb..125ee56 100644
--- a/ansible/accumulo.yml
+++ b/ansible/accumulo.yml
@@ -19,7 +19,7 @@
tasks:
- import_tasks: roles/accumulo/tasks/download.yml
when: download_software
-- hosts: all
+- hosts: all:!{{ azure_proxy_host }}
roles:
- accumulo
- hosts: accumulomaster
diff --git a/ansible/azure.yml b/ansible/azure.yml
new file mode 100644
index 0000000..c587558
--- /dev/null
+++ b/ansible/azure.yml
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+---
+- hosts: localhost
+ roles:
+ - azure
diff --git a/ansible/common.yml b/ansible/common.yml
index ef26613..744ec5e 100644
--- a/ansible/common.yml
+++ b/ansible/common.yml
@@ -15,16 +15,18 @@
# limitations under the License.
#
+- hosts: proxy
+ become: yes
+ roles:
+ - proxy
+ tasks:
+ - import_tasks: roles/proxy/tasks/main.yml
+ - import_tasks: roles/proxy/tasks/download.yml
+ when: download_software
- hosts: nodes
become: yes
tasks:
- import_tasks: roles/common/tasks/hosts.yml
-- hosts: proxy
- roles:
- - proxy
- tasks:
- - import_tasks: roles/proxy/tasks/download.yml
- when: download_software
- hosts: all
become: yes
roles:
@@ -32,6 +34,8 @@
tasks:
- import_tasks: roles/common/tasks/ssh.yml
- import_tasks: roles/common/tasks/os.yml
+ - import_tasks: roles/common/tasks/azure.yml
+ when: cluster_type == 'azure'
- import_tasks: roles/common/tasks/ec2.yml
when: cluster_type == 'ec2'
- import_tasks: roles/common/tasks/existing.yml
diff --git a/ansible/conf/ansible.cfg b/ansible/conf/ansible.cfg
index 2ba1c3e..e99d4ed 100644
--- a/ansible/conf/ansible.cfg
+++ b/ansible/conf/ansible.cfg
@@ -17,3 +17,5 @@
host_key_checking = False
forks = 50
gathering = smart
+callback_whitelist = profile_tasks
+timeout=30
diff --git a/ansible/docker.yml b/ansible/docker.yml
index 83870be..0e893ad 100644
--- a/ansible/docker.yml
+++ b/ansible/docker.yml
@@ -15,7 +15,7 @@
# limitations under the License.
#
-- hosts: all
+- hosts: all:!{{ azure_proxy_host }}
become: yes
roles:
- docker
diff --git a/ansible/hadoop.yml b/ansible/hadoop.yml
index ec88699..30fd7f7 100644
--- a/ansible/hadoop.yml
+++ b/ansible/hadoop.yml
@@ -15,7 +15,7 @@
# limitations under the License.
#
-- hosts: all
+- hosts: all:!{{ azure_proxy_host }}
roles:
- hadoop
- hosts: namenode
diff --git a/ansible/mesos.yml b/ansible/mesos.yml
index fd8d8d0..60d6527 100644
--- a/ansible/mesos.yml
+++ b/ansible/mesos.yml
@@ -15,7 +15,7 @@
# limitations under the License.
#
-- hosts: all
+- hosts: all:!{{ azure_proxy_host }}
become: yes
roles:
- mesos
diff --git a/ansible/roles/azure/tasks/create_vmss.yml b/ansible/roles/azure/tasks/create_vmss.yml
new file mode 100644
index 0000000..1feb360
--- /dev/null
+++ b/ansible/roles/azure/tasks/create_vmss.yml
@@ -0,0 +1,243 @@
+---
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# These Ansible tasks only run on the client machine where Muchos runs
+# At a high level, the various sections in this file do the following:
+# 1. Create (if not already existing): an Azure resource group, virtual network / subnet
+# 2. Optionally (if the user specified) create a VM and related resources to use as a proxy host
+# 3. Create the Azure VMSS to support the nodes for use with Muchos
+# 4. Automatically populate the hosts file and associated [nodes] section in muchos.props
+
+# SECTION 1: Create Azure RG, network and subnet
+- name: Create a resource group
+ azure_rm_resourcegroup:
+ name: "{{ resource_group }}"
+ location: "{{ location }}"
+ tags:
+ deployment_type: muchos
+ application: accumulo
+
+- name: Create a virtual network
+ azure_rm_virtualnetwork:
+ resource_group: "{{ resource_group }}"
+ name: "{{ vnet }}"
+ address_prefixes_cidr:
+ - "{{ vnet_cidr }}"
+ tags:
+ deployment_type: muchos
+ application: accumulo
+
+- name: Create a subnet
+ azure_rm_subnet:
+ resource_group: "{{ resource_group }}"
+ virtual_network_name: "{{ vnet }}"
+ name: "{{ subnet }}"
+ address_prefix_cidr: "{{ subnet_cidr }}"
+
+# SECTION 2: Optionally create a VM with a public IP which can act as a proxy host
+- name: Create public IP address
+ azure_rm_publicipaddress:
+ resource_group: "{{ resource_group }}"
+ allocation_method: Static
+ name: "{{ azure_proxy_host }}-ip"
+ register: azure_proxy_public_ip
+ when: azure_proxy_host is defined and azure_proxy_host != '' and azure_proxy_host != None
+
+- name: Create Network Security Group that allows SSH
+ azure_rm_securitygroup:
+ resource_group: "{{ resource_group }}"
+ name: "{{ azure_proxy_host }}-nsg"
+ rules:
+ - name: SSH
+ protocol: Tcp
+ destination_port_range: 22
+ access: Allow
+ priority: 1001
+ direction: Inbound
+ when: azure_proxy_host is defined and azure_proxy_host != '' and azure_proxy_host != None
+
+- name: Create NIC
+ azure_rm_networkinterface:
+ resource_group: "{{ resource_group }}"
+ name: "{{ azure_proxy_host }}-nic"
+ virtual_network: "{{ vnet }}"
+ subnet: "{{ subnet }}"
+ public_ip_name: "{{ azure_proxy_host }}-ip"
+ security_group: "{{ azure_proxy_host }}-nsg"
+ when: azure_proxy_host is defined and azure_proxy_host != '' and azure_proxy_host != None
+
+- name: Create azure proxy virtual machine
+ azure_rm_virtualmachine:
+ resource_group: "{{ resource_group }}"
+ name: "{{ azure_proxy_host }}"
+ network_interface_names:
+ - "{{ azure_proxy_host }}-nic"
+ vm_size: Standard_D8s_v3
+ admin_username: "{{ admin_username }}"
+ ssh_password_enabled: false
+ ssh_public_keys:
+ - path: /home/{{admin_username}}/.ssh/authorized_keys
+ key_data: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
+ os_disk_caching: ReadWrite
+ image:
+ offer: CentOS
+ publisher: OpenLogic
+ sku: 7.5
+ version: latest
+ data_disks:
+ - lun: 0
+ disk_size_gb: 64
+ when: azure_proxy_host is defined and azure_proxy_host != '' and azure_proxy_host != None
+
+# SECTION 3: Create the Azure VMSS for the nodes used by Muchos
+- name: Create luns dictionary
+ set_fact:
+ luns_dict: "{{ luns_dict | default ([]) + [{ 'lun': item, 'disk_size_gb': disk_size_gb , 'caching': None } ] }}"
+ with_sequence: start=0 end={{numdisks-1}}
+
+- name: Set single placement group to correct value
+ set_fact:
+ single_placement_group: False
+ when: numnodes > 100
+
+- name: Create Scale Set
+ azure_rm_virtualmachinescaleset:
+ resource_group: "{{ resource_group }}"
+ location: "{{ location }}"
+ name: "{{ vmss_name }}"
+ vm_size: "{{ vm_sku }}"
+ admin_username: "{{ admin_username }}"
+ ssh_password_enabled: false
+ ssh_public_keys:
+ - path: /home/{{admin_username}}/.ssh/authorized_keys
+ key_data: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
+ capacity: "{{ numnodes }}"
+ virtual_network_name: "{{ vnet }}"
+ subnet_name: "{{ subnet }}"
+ upgrade_policy: Manual
+ tier: Standard
+ managed_disk_type: "{{ managed_disk_type }}"
+ os_disk_caching: ReadWrite
+ enable_accelerated_networking: yes
+ single_placement_group: "{{ single_placement_group | default(omit) }}"
+ image:
+ offer: CentOS
+ publisher: OpenLogic
+ sku: 7.5
+ version: latest
+ data_disks: "{{ luns_dict }}"
+ tags: create_vmss
+
+# SECTION 4: Automatically populate entries in the hosts file and in the muchos.props file, based on the VMSS node details
+- name: get instance ids
+ shell: "az vmss list-instances --resource-group {{ resource_group }} --name {{ vmss_name }} --query '[].[{name: name,vmresourceid: id}]' -o json | sed -e 's/{{vmss_name}}_/{{vmss_name}}-/g'"
+ register: instance_list
+- set_fact:
+ instances: "{{ instance_list.stdout|from_json }}"
+
+- name: get private ip addresses
+ command: "az vmss nic list --resource-group {{ resource_group }} --vmss-name {{ vmss_name }} --query '[].[{ip: ipConfigurations[0].privateIpAddress, vmresourceid: virtualMachine.id}]' -o json"
+ register: ip_list
+- set_fact:
+ addresses: "{{ ip_list.stdout|from_json }}"
+- set_fact:
+ instances_dict: "{{ instances | json_query('[].{key: vmresourceid, value: name}') }}"
+ addresses_dict: "{{ addresses | json_query('[].{key: vmresourceid, value: ip}') }}"
+
+- name: Ensures hosts sub-dir exists
+ file:
+ path: "{{ deploy_path }}/conf/hosts/"
+ state: directory
+ recurse: yes
+
+- name: Ensure empty hosts file is created
+ copy:
+ content: ""
+ dest: "{{ deploy_path }}/conf/hosts/{{ vmss_name }}"
+ force: yes
+ mode: 0644
+
+- name: Write azure proxy to hosts file
+ lineinfile:
+ path: "{{ deploy_path }}/conf/hosts/{{ vmss_name }}"
+ line: "{{ azure_proxy_host }} {{ azure_proxy_public_ip.state.ip_address }}"
+ when: azure_proxy_host is defined and azure_proxy_host != '' and azure_proxy_host != None
+
+- name: Write VMSS vms to hosts file
+ lineinfile:
+ path: "{{ deploy_path }}/conf/hosts/{{ vmss_name }}"
+ line: "{{ item1[0] }} {{ item2[0] }}"
+ with_items: "{{ instances_dict | json_query('[].key')}}"
+ vars:
+ myquery: "[?key=='{{ item }}'].value"
+ item1: "{{ instances_dict | json_query(myquery) }}"
+ item2: "{{ addresses_dict | json_query(myquery) }}"
+
+- name: Clear section
+ ini_file:
+ path: "{{ deploy_path }}/conf/muchos.props"
+ section: "nodes"
+ state: absent
+
+- name: Recreate section
+ ini_file:
+ path: "{{ deploy_path }}/conf/muchos.props"
+ section: "nodes"
+ option: "#host0"
+ value: "service"
+ state: present
+
+- name: add azure proxy host
+ lineinfile:
+ path: "{{ deploy_path }}/conf/muchos.props"
+ line: "{{ azure_proxy_host }} = client"
+ when: azure_proxy_host is defined and azure_proxy_host != '' and azure_proxy_host != None
+
+- name: Assign Accumulo master, HDFS components to the first node of the cluster
+ lineinfile:
+ path: "{{ deploy_path }}/conf/muchos.props"
+ line: "{{ item }} = namenode,resourcemanager,accumulomaster,zookeeper"
+ with_items: "{{ instances_dict | json_query('[0].value') }}"
+
+- name: Assign metrics to the second node of the cluster
+ lineinfile:
+ path: "{{ deploy_path }}/conf/muchos.props"
+ line: "{{ item }} = metrics"
+ with_items: "{{ instances_dict | json_query('[1].value') }}"
+
+- name: Add worker nodes to muchos.props
+ lineinfile:
+ path: "{{ deploy_path }}/conf/muchos.props"
+ line: "{{ item }} = worker"
+ with_items: "{{ instances_dict | json_query('[2:].value') }}"
+
+- name: Change proxy hostname to azure proxy host in muchos.props
+ lineinfile:
+ path: "{{ deploy_path }}/conf/muchos.props"
+ regexp: '^proxy_hostname\s*=\s*'
+ line: "proxy_hostname = {{ azure_proxy_host }}"
+ when: azure_proxy_host is defined and azure_proxy_host != '' and azure_proxy_host != None
+
+- name: Change proxy hostname to first node in vmss in muchos.props
+ lineinfile:
+ path: "{{ deploy_path }}/conf/muchos.props"
+ regexp: '^proxy_hostname\s*=\s*'
+ line: "proxy_hostname = {{ item }}"
+ with_items: "{{ instances_dict | json_query('[0].value') }}"
+ when: not (azure_proxy_host is defined and azure_proxy_host != '' and azure_proxy_host != None)
diff --git a/ansible/roles/azure/tasks/main.yml b/ansible/roles/azure/tasks/main.yml
new file mode 100644
index 0000000..6ec80d7
--- /dev/null
+++ b/ansible/roles/azure/tasks/main.yml
@@ -0,0 +1,21 @@
+---
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# tasks file for azure
+- import_tasks: create_vmss.yml
diff --git a/ansible/roles/common/tasks/azure.yml b/ansible/roles/common/tasks/azure.yml
new file mode 100644
index 0000000..bcbe967
--- /dev/null
+++ b/ansible/roles/common/tasks/azure.yml
@@ -0,0 +1,66 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+- name: Find luns
+ find:
+ paths: "/dev/disk/azure/scsi1"
+ patterns: "lun*"
+ file_type: link
+ register: files_matched
+- name: Create xfs filesytems
+ filesystem:
+ fstype: xfs
+ dev: "{{ item.path }}"
+ with_items: "{{ files_matched.files }}"
+- name: Get UUID
+ command: "blkid {{ item.path }} -s UUID -o value"
+ with_items: "{{ files_matched.files }}"
+ register: disk_uuids
+- name: Create mount points
+ file:
+ path: '{{ mount_root }}{{ item.0 + 1 }}'
+ state: directory
+ with_indexed_items: "{{ files_matched.files }}"
+- name: Mount filesystems
+ mount:
+ path: '{{ mount_root }}{{ item.0 + 1 }}'
+ src: "UUID={{ item.1.stdout }}"
+ fstype: xfs
+ state: mounted
+ with_indexed_items: "{{ disk_uuids.results }}"
+- name: Set mount point ownership
+ file:
+ path: '{{ mount_root }}{{ item.0 + 1 }}'
+ state: directory
+ owner: "{{ cluster_user }}"
+ group: "{{ cluster_group }}"
+ with_indexed_items: "{{ files_matched.files }}"
+- name: Create directry to mount Azure File share
+ file:
+ path: "{{ azure_fileshare_mount }}"
+ state: directory
+ owner: '{{ cluster_user }}'
+ group: '{{ cluster_group }}'
+ when: azure_fileshare_mount is defined and azure_fileshare_mount != '' and azure_fileshare_mount != None
+- name: Mount Azure File share
+ become: yes
+ mount:
+ fstype: cifs
+ src: "{{ azure_fileshare }}"
+ path: "{{ azure_fileshare_mount }}"
+ opts: vers=3.0,username={{ azure_fileshare_username }},password={{ azure_fileshare_password }},dir_mode=0777,file_mode=0777,serverino
+ state: mounted
+ when: azure_fileshare_mount is defined and azure_fileshare_mount != '' and azure_fileshare_mount != None
diff --git a/ansible/roles/common/tasks/hosts.yml b/ansible/roles/common/tasks/hosts.yml
index 1e3d68b..abb2d3a 100644
--- a/ansible/roles/common/tasks/hosts.yml
+++ b/ansible/roles/common/tasks/hosts.yml
@@ -18,4 +18,4 @@
- name: "ensure hostname is correct"
hostname: name={{ inventory_hostname }}
- name: "ensure /etc/hosts is correct"
- template: src=roles/common/templates/etc_hosts dest=/etc/hosts owner=root group=root mode=0644
+ copy: src=/etc/etc_hosts dest=/etc/hosts owner=root group=root mode=0644
diff --git a/ansible/roles/common/tasks/os.yml b/ansible/roles/common/tasks/os.yml
index 8e6f02b..83df0df 100644
--- a/ansible/roles/common/tasks/os.yml
+++ b/ansible/roles/common/tasks/os.yml
@@ -17,6 +17,24 @@
- name: "set swappiness to zero"
sysctl: name=vm.swappiness value=0
+
+# The kernel memory settings below have been added based on performance testing done
+# with Microsoft Azure based clusters. For more details please refer to
+# https://access.redhat.com/solutions/90883 and https://bugzilla.kernel.org/show_bug.cgi?id=107111
+- name: "set vm.min_free_kbytes to 1G"
+ sysctl: name=vm.min_free_kbytes value=1000000
+ when: cluster_type == 'azure' or cluster_type == 'ec2'
+- name: "set vm.zone_reclaim_mode to 1"
+ sysctl: name=vm.zone_reclaim_mode value=1
+ when: cluster_type == 'azure' or cluster_type == 'ec2'
+
+# The TCP/IP setting below has been added based on performance testing done
+# with Microsoft Azure based clusters. For more details refer to
+# https://access.redhat.com/solutions/30453
+- name: "set net.core.somaxconn=2048"
+ sysctl: name=net.core.somaxconn value=2048
+ when: cluster_type == 'azure' or cluster_type == 'ec2'
+
- name: "remove old limits file"
file: path=/etc/security/limits.d/20-nproc.conf state=absent
- name: "copy new limits.conf"
diff --git a/ansible/roles/proxy/tasks/main.yml b/ansible/roles/proxy/tasks/main.yml
index 21a7fa5..9a4fe07 100644
--- a/ansible/roles/proxy/tasks/main.yml
+++ b/ansible/roles/proxy/tasks/main.yml
@@ -15,8 +15,10 @@
# limitations under the License.
#
-- name: "ensure cluster user exists and generate ssh key"
+- name: "ensure cluster user exists and generate ssh key"
user: name={{ cluster_user }} generate_ssh_key=yes ssh_key_bits=4096 state=present
become: yes
- name: "create tarball directory on proxy"
file: path={{ tarballs_dir }} state=directory
+- name: "copy /etc/hosts to proxy"
+ template: src=roles/proxy/templates/etc_hosts dest=/etc/etc_hosts owner=root group=root mode=0644
diff --git a/ansible/roles/proxy/templates/etc_hosts b/ansible/roles/proxy/templates/etc_hosts
new file mode 100644
index 0000000..d8c79db
--- /dev/null
+++ b/ansible/roles/proxy/templates/etc_hosts
@@ -0,0 +1,6 @@
+127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
+::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
+
+{% for host in groups['nodes'] %}
+{{ hostvars[host]['ansible_ssh_host'] }} {{ host }}
+{% endfor %}
diff --git a/ansible/spark.yml b/ansible/spark.yml
index adf62b1..40d01db 100644
--- a/ansible/spark.yml
+++ b/ansible/spark.yml
@@ -19,7 +19,7 @@
tasks:
- import_tasks: roles/spark/tasks/download.yml
when: download_software
-- hosts: all
+- hosts: all:!{{ azure_proxy_host }}
roles:
- spark
- hosts: spark
diff --git a/ansible/zookeeper.yml b/ansible/zookeeper.yml
index 3e625ed..650e524 100644
--- a/ansible/zookeeper.yml
+++ b/ansible/zookeeper.yml
@@ -15,7 +15,7 @@
# limitations under the License.
#
-- hosts: all
+- hosts: all:!{{ azure_proxy_host }}
roles:
- zookeeper
- hosts: zookeepers
diff --git a/conf/checksums b/conf/checksums
index 9b130e3..2b2d38e 100644
--- a/conf/checksums
+++ b/conf/checksums
@@ -8,15 +8,20 @@
fluo:1.2.0:037f89cd2bfdaf76a1368256c52de46d6b9a85c9c1bfc776ec4447d02c813fb2
fluo_yarn:1.0.0:c6220d35cf23127272f3b5638c44586504dc17a46f5beecdfee5027b5ff874b0
hadoop:3.2.0:226b6cbdf769467250054b3abdf26df9f05fde44bbb82fe5d12d6993ea848f64
+hadoop:3.1.2:1C02CCC60A09C63A48DC4234FFD3AED1B75E5A1F2B49D60927EDA114B93DD31A
hadoop:3.1.1:f837fe260587f71629aad1f4fb6719274e948111dc96ffc5a8e26f27deac5602
hadoop:3.0.2:0d507aa71007b2685e292343c11c2cb90a92ea7625446b57d1fb47c5721e2f82
+hadoop:3.0.3:db96e2c0d0d5352d8984892dfac4e27c0e682d98a497b7e04ee97c3e2019277a
hadoop:2.9.2:3d2023c46b1156c1b102461ad08cbc17c8cc53004eae95dab40a1f659839f28a
hadoop:2.9.0:8d48666f29f9ade6ed2762b7a9edab177bad2c57396f43d0ffd6a269d54f6fe1
+hadoop:2.8.5:F9C726DF693CE2DAA4107886F603270D66E7257F77A92C9886502D6CD4A884A4
hadoop:2.8.4:6b545972fdd73173887cdbc3e1cbd3cc72068271924edea82a0e7e653199b115
hadoop:2.8.3:e8bf9a53337b1dca3b152b0a5b5e277dc734e76520543e525c301a050bb27eae
hadoop:2.7.6:f2327ea93f4bc5a5d7150dee8e0ede196d3a77ff8526a7dd05a48a09aae25669
hadoop:2.7.5:0bfc4d9b04be919be2fdf36f67fa3b4526cdbd406c512a7a1f5f1b715661f831
hadoop:2.6.5:001ad18d4b6d0fe542b15ddadba2d092bc97df1c4d2d797381c8d12887691898
+spark:2.4.3:4db62e110c8080f28ac8f1d701b7477cede23e60b51231ab63687b2cc6150faa
+spark:2.3.3:a5c4a54db5163df25110534f36e13d6ae65758d45d1bc8bfcd7e27304e716a23
spark:2.3.2:3387107155d62f04ccf6bcaf2e00a69a0de5ae5df875348d93147743c206f0a8
spark:2.2.2:023b2fea378b3dd0fee2d5d1de6bfaf2d8349aefe7be97a9cbcf03bbacc428d7
zookeeper:3.4.14:b14f7a0fece8bd34c7fffa46039e563ac5367607c612517aa7bd37306afbd1cd
diff --git a/conf/muchos.props.example b/conf/muchos.props.example
index 599c812..2615af4 100644
--- a/conf/muchos.props.example
+++ b/conf/muchos.props.example
@@ -14,7 +14,7 @@
# limitations under the License.
[general]
-# Cluster type (ec2 or existing)
+# Cluster type (Azure, ec2, or existing)
cluster_type = ec2
# Cluster user name (install command will SSH to cluster using this user)
# Leave default below if launching cluster in AWS
@@ -108,6 +108,29 @@
# Below are different performance profiles that can be selected. Each profile
# has the same properties with different values.
+[azure]
+resource_group = accumulo-rg
+vnet = vnet1
+vnet_cidr = "10.0.0.0/8"
+subnet = subnet1
+subnet_cidr = "10.1.0.0/16"
+numnodes = 8
+vm_sku = Standard_D8s_v3
+managed_disk_type = Standard_LRS
+numdisks = 3
+disk_size_gb = 128
+mount_root = /var/data
+metrics_drive_root = var-data
+# Optional proxy VM. If not set, the first node of the cluster will be selected as the proxy.
+azure_proxy_host =
+location = westus2
+# Optional Azure fileshare to mount on all nodes.
+# Path and credentials must be updated to enable this.
+#azure_fileshare_mount = /mnt/azure-fileshare
+#azure_fileshare = //fileshare-to-mount.file.core.windows.net/path
+#azure_fileshare_username = fs_username
+#azure_fileshare_password = fs_password
+
[perf-small]
# Amount of JVM heap for each tserver
accumulo_tserv_mem=2G
@@ -156,18 +179,42 @@
fluo_worker_instances_multiplier=2
yarn_nm_mem_mb=16384
+[azd16s]
+accumulo_tserv_mem=4G
+accumulo_dcache_size=2G
+accumulo_icache_size=1G
+accumulo_imap_size=512M
+fluo_worker_mem_mb=4096
+twill_reserve_mem_mb=512
+fluo_worker_threads=64
+fluo_worker_instances_multiplier=2
+yarn_nm_mem_mb=16384
+
+[azd8s]
+accumulo_tserv_mem=4G
+accumulo_dcache_size=2G
+accumulo_icache_size=1G
+accumulo_imap_size=512M
+fluo_worker_mem_mb=4096
+twill_reserve_mem_mb=512
+fluo_worker_threads=64
+fluo_worker_instances_multiplier=2
+yarn_nm_mem_mb=16384
+
[ansible-vars]
# This section is used to override Ansible variables. Any variable set below will be placed in the hosts file created by Muchos.
# Expected format: variable = value
[nodes]
-# Describes nodes in cluster in the following format:
+# If cluster_type=existing, the list of nodes below needs to manually populated with a list of cluster nodes in the following format:
# <Hostname> = <Service1>[,<Service2>,<Service3>]
# Where:
# Hostname = Must be unique. Will be used for hostname in EC2 or should match hostname on your own cluster
# Service = Service to run on node (possible values: zookeeper, namenode, resourcemanager, accumulomaster, client, swarmmanager,
# mesosmaster, worker, fluo, metrics, spark). The following services are required: namenode, resourcemanager,
# accumulomaster, zookeeper & worker
+# If cluster_type=azure, the list of nodes below is auto-generated by the launch action e.g. "muchos launch --cluster accumuloclstr"
+# For the 'azure' cluster type, it is perfectly normal if the auto-generated list of node names is not sequential
leader1 = namenode,resourcemanager,accumulomaster,zookeeper
leader2 = metrics
worker1 = worker,swarmmanager
diff --git a/lib/main.py b/lib/main.py
index 62df08c..e2149c6 100644
--- a/lib/main.py
+++ b/lib/main.py
@@ -72,6 +72,10 @@
else:
cluster = Ec2Cluster(config)
cluster.perform(action)
+ elif cluster_type == 'azure':
+ from muchos.azure import VmssCluster
+ cluster = VmssCluster(config)
+ cluster.perform(action)
else:
exit('Unknown cluster_type: ' + cluster_type)
diff --git a/lib/muchos/azure.py b/lib/muchos/azure.py
new file mode 100644
index 0000000..0b3d3ea
--- /dev/null
+++ b/lib/muchos/azure.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import json
+import subprocess
+from os.path import join
+from .existing import ExistingCluster
+from azure.common.client_factory import get_client_from_cli_profile
+from azure.mgmt.compute import ComputeManagementClient
+
+
+# For Muchos deployments on Microsoft Azure, we use Virtual Machine Scale Sets
+# VMSS is the most efficient way to provision large numbers of VMs in Azure
+class VmssCluster(ExistingCluster):
+
+ def __init__(self, config):
+ ExistingCluster.__init__(self, config)
+
+ def launch(self):
+ config = self.config
+ azure_config = dict(config.items("azure"))
+ azure_config["admin_username"] = config.get("general", "cluster_user")
+ azure_config["vmss_name"] = config.cluster_name
+ azure_config["deploy_path"] = config.deploy_path
+ azure_config = {k: int(v) if v.isdigit() else v
+ for k, v in azure_config.items()}
+ subprocess.call(["ansible-playbook",
+ join(config.deploy_path, "ansible/azure.yml"),
+ "--extra-vars", json.dumps(azure_config)])
+
+ def status(self):
+ config = self.config
+ azure_vars_dict = dict(config.items("azure"))
+ compute_client = get_client_from_cli_profile(ComputeManagementClient)
+ vmss_status = compute_client.virtual_machine_scale_sets.get(
+ azure_vars_dict["resource_group"],
+ self.config.cluster_name)
+ print('name:', vmss_status.name,
+ '\nprovisioning_state:', vmss_status.provisioning_state)
diff --git a/lib/muchos/config.py b/lib/muchos/config.py
index 1b0c196..b0f004b 100644
--- a/lib/muchos/config.py
+++ b/lib/muchos/config.py
@@ -50,11 +50,13 @@
def verify_config(self, action):
proxy = self.get('general', 'proxy_hostname')
- if not proxy:
- exit("ERROR - proxy.hostname must be set in muchos.props")
+ cluster_type = self.get('general', 'cluster_type')
+ if cluster_type not in ['azure']:
+ if not proxy:
+ exit("ERROR - proxy.hostname must be set in muchos.props")
- if proxy not in self.node_d:
- exit("ERROR - The proxy (set by property proxy_hostname={0}) cannot be found in 'nodes' section of "
+ if proxy not in self.node_d:
+ exit("ERROR - The proxy (set by property proxy_hostname={0}) cannot be found in 'nodes' section of "
"muchos.props".format(proxy))
if action in ['launch', 'setup']:
@@ -120,6 +122,8 @@
return '/media/' + self.ephemeral_root
elif self.get_cluster_type() == 'existing':
return self.get('existing', 'mount_root')
+ elif self.get_cluster_type() == 'azure':
+ return self.get('azure', 'mount_root')
def fstype(self):
retval = None
@@ -137,17 +141,27 @@
return 'no'
return retval
- def worker_data_dirs(self):
+ def data_dirs_common(self, nodeType):
+ data_dirs = []
+
if self.get_cluster_type() == 'ec2':
- return self.node_type_map()['worker']['mounts']
+ data_dirs = self.node_type_map()[nodeType]['mounts']
elif self.get_cluster_type() == 'existing':
- return self.get('existing', 'data_dirs').split(",")
+ data_dirs = self.get('existing', 'data_dirs').split(",")
+ elif self.get_cluster_type() == 'azure':
+ num_disks = int(self.get("azure","numdisks"))
+ range_var = num_disks + 1
+ for diskNum in range(1, range_var):
+ data_dirs.append(self.get("azure","mount_root") + str(diskNum))
+
+
+ return data_dirs
+
+ def worker_data_dirs(self):
+ return self.data_dirs_common("worker")
def default_data_dirs(self):
- if self.get_cluster_type() == 'ec2':
- return self.node_type_map()['default']['mounts']
- elif self.get_cluster_type() == 'existing':
- return self.get('existing', 'data_dirs').split(",")
+ return self.data_dirs_common("default")
def metrics_drive_ids(self):
if self.get_cluster_type() == 'ec2':
@@ -157,6 +171,12 @@
return drive_ids
elif self.get_cluster_type() == 'existing':
return self.get("existing", "metrics_drive_ids").split(",")
+ elif self.get_cluster_type() == 'azure':
+ drive_ids = []
+ range_var = int(self.get("azure","numdisks")) + 1
+ for i in range(1, range_var):
+ drive_ids.append(self.get("azure","metrics_drive_root") + str(i))
+ return drive_ids
def shutdown_delay_minutes(self):
retval = '0'
@@ -292,7 +312,7 @@
return self.get_hosts()[hostname][1]
def get_cluster_type(self):
- if self.cluster_type not in ('ec2', 'existing'):
+ if self.cluster_type not in ('azure', 'ec2', 'existing'):
exit('ERROR - Unknown cluster type' + self.cluster_type)
return self.cluster_type
@@ -327,6 +347,9 @@
for (name, val) in self.items('ec2'):
print(name, '=', val)
+ for (name, val) in self.items('azure'):
+ print(name, '=', val)
+
def print_property(self, key):
if key == 'proxy.public.ip':
print(self.proxy_public_ip())
@@ -474,3 +497,10 @@
'yarn_nm_mem_mb': None,
'zookeeper_sha256': None
}
+
+AZURE_VAR_DEFAULTS = {
+ 'azure_fileshare_mount': None,
+ 'azure_fileshare': None,
+ 'azure_fileshare_username': None,
+ 'azure_fileshare_password': None,
+}
diff --git a/lib/muchos/existing.py b/lib/muchos/existing.py
index 6b9247a..5253d6d 100644
--- a/lib/muchos/existing.py
+++ b/lib/muchos/existing.py
@@ -23,7 +23,7 @@
from sys import exit
from os import listdir
-from .config import HOST_VAR_DEFAULTS, PLAY_VAR_DEFAULTS
+from .config import HOST_VAR_DEFAULTS, PLAY_VAR_DEFAULTS, AZURE_VAR_DEFAULTS
class ExistingCluster:
@@ -41,13 +41,17 @@
host_vars = HOST_VAR_DEFAULTS
play_vars = PLAY_VAR_DEFAULTS
- for section in ("general", "ansible-vars", config.get('performance', 'profile')):
+ azure_vars = AZURE_VAR_DEFAULTS
+
+ for section in ("general", "ansible-vars", config.get('performance', 'profile'), "azure"):
for (name, value) in config.items(section):
if name not in ('proxy_hostname', 'proxy_socks_port'):
if name in host_vars:
host_vars[name] = value
if name in play_vars:
play_vars[name] = value
+ if name in azure_vars:
+ azure_vars[name] = value
play_vars['accumulo_sha256'] = config.checksum('accumulo')
play_vars['fluo_sha256'] = config.checksum('fluo')
@@ -66,10 +70,13 @@
with open(join(config.deploy_path, "ansible/site.yml"), 'w') as site_file:
print("- import_playbook: common.yml", file=site_file)
+
+ print("- import_playbook: zookeeper.yml", file=site_file)
+ print("- import_playbook: hadoop.yml", file=site_file)
+
if config.has_service("spark"):
print("- import_playbook: spark.yml", file=site_file)
- print("- import_playbook: hadoop.yml", file=site_file)
- print("- import_playbook: zookeeper.yml", file=site_file)
+
if config.has_service("metrics"):
print("- import_playbook: metrics.yml", file=site_file)
print("- import_playbook: accumulo.yml", file=site_file)
@@ -127,6 +134,8 @@
print("\n[all:vars]", file=hosts_file)
for (name, value) in sorted(host_vars.items()):
print("{0} = {1}".format(name, value), file=hosts_file)
+ for (name, value) in sorted(azure_vars.items()):
+ print("{0} = {1}".format(name, value), file=hosts_file)
with open(join(config.deploy_path, "ansible/group_vars/all"), 'w') as play_vars_file:
for (name, value) in sorted(play_vars.items()):
@@ -210,8 +219,10 @@
def execute_playbook(self, playbook):
print("Executing '{0}' playbook".format(playbook))
- self.exec_on_proxy_verified("time -p ansible-playbook {base}/ansible/{playbook}"
- .format(base=self.config.user_home(), playbook=playbook), opts='-t')
+ azure_proxy_host = self.config.get("azure","azure_proxy_host")
+ var_azure_proxy_host = "_" if (azure_proxy_host==None or azure_proxy_host.strip()=='') else azure_proxy_host
+ self.exec_on_proxy_verified("time -p ansible-playbook {base}/ansible/{playbook} --extra-vars \"azure_proxy_host={var_azure_proxy_host}\""
+ .format(base=self.config.user_home(), playbook=playbook, var_azure_proxy_host=var_azure_proxy_host), opts='-t')
def send_to_proxy(self, path, target, skip_if_exists=True):
print("Copying to proxy: ", path)
diff --git a/lib/muchos/util.py b/lib/muchos/util.py
index 202544b..0a665d0 100644
--- a/lib/muchos/util.py
+++ b/lib/muchos/util.py
@@ -142,8 +142,8 @@
parser = OptionParser(
usage="muchos [options] <action>\n\n"
+ "where <action> can be:\n"
- + " launch Launch cluster in EC2\n"
- + " status Check status of EC2 cluster\n"
+ + " launch Launch cluster in Azure or EC2\n"
+ + " status Check status of Azure or EC2 cluster\n"
+ " setup Set up cluster\n"
+ " sync Sync ansible directory on cluster proxy node\n"
+ " config Print configuration for that cluster. Requires '-p'. Use '-p all' for all config.\n"
diff --git a/lib/tests/test_config.py b/lib/tests/test_config.py
index 3d78275..f6501ff 100644
--- a/lib/tests/test_config.py
+++ b/lib/tests/test_config.py
@@ -46,6 +46,7 @@
assert c.instance_tags() == {}
assert len(c.nodes()) == 6
assert c.get_node('leader1') == ['namenode', 'resourcemanager', 'accumulomaster', 'zookeeper']
+ assert c.get_node('leader2') == ['metrics']
assert c.get_node('worker1') == ['worker', 'swarmmanager']
assert c.get_node('worker2') == ['worker']
assert c.get_node('worker3') == ['worker']
@@ -74,7 +75,77 @@
('10.0.0.4', 'worker3'), ('10.0.0.5', 'worker4')]
assert c.get_host_services() == [('leader1', 'namenode resourcemanager accumulomaster zookeeper'),
('leader2', 'metrics'),
- ('worker1', 'worker swarmmanager'), ('worker2', 'worker'), ('worker3', 'worker'),
+ ('worker1', 'worker swarmmanager'),
+ ('worker2', 'worker'),
+ ('worker3', 'worker'),
+ ('worker4', 'worker')]
+
+
+def test_azure_cluster():
+ c = DeployConfig("muchos", '../conf/muchos.props.example', '../conf/hosts/example/example_cluster',
+ '../conf/checksums', '../conf/templates', 'mycluster')
+
+ # since we are sharing a single muchos.props.example file, we need
+ # to stub the cluster type to be azure (as the file itself has a default of ec2)
+
+ c.cluster_type = 'azure'
+
+ assert c.checksum_ver('accumulo', '1.9.0') == 'f68a6145029a9ea843b0305c90a7f5f0334d8a8ceeea94734267ec36421fe7fe'
+ assert c.checksum('accumulo') == 'df172111698c7a73aa031de09bd5589263a6b824482fbb9b4f0440a16602ed47'
+ assert c.get('azure', 'vm_sku') == 'Standard_D8s_v3'
+ assert c.get('azure', 'managed_disk_type') == 'Standard_LRS'
+ assert c.user_home() == '/home/centos'
+ assert c.mount_root() == '/var/data'
+ assert c.force_format() == 'no'
+ assert c.worker_data_dirs() == ['/var/data1', '/var/data2', '/var/data3']
+ assert c.default_data_dirs() == ['/var/data1', '/var/data2', '/var/data3']
+ assert c.metrics_drive_ids() == ['var-data1', 'var-data2', 'var-data3']
+ assert c.shutdown_delay_minutes() == '0'
+ assert c.mounts(2) == ['/var/data0', '/var/data1']
+ assert c.node_type('worker1') == 'worker'
+ assert c.node_type('leader1') == 'default'
+ assert c.has_option('azure', 'resource_group')
+ assert c.has_option('azure', 'vnet')
+ assert c.has_option('azure', 'vnet_cidr')
+ assert c.has_option('azure', 'subnet')
+ assert c.has_option('azure', 'subnet_cidr')
+ assert c.has_option('azure', 'numnodes')
+ assert c.has_option('azure', 'location')
+ assert c.instance_tags() == {}
+ assert len(c.nodes()) == 6
+ assert c.get_node('leader1') == ['namenode', 'resourcemanager', 'accumulomaster', 'zookeeper']
+ assert c.get_node('leader2') == ['metrics']
+ assert c.get_node('worker1') == ['worker', 'swarmmanager']
+ assert c.get_node('worker2') == ['worker']
+ assert c.get_node('worker3') == ['worker']
+ assert c.has_service('accumulomaster')
+ assert not c.has_service('fluo')
+ assert c.get_service_hostnames('worker') == ['worker1', 'worker2', 'worker3', 'worker4']
+ assert c.get_service_hostnames('zookeeper') == ['leader1']
+ assert c.get_hosts() == {'leader2': ('10.0.0.1', None), 'leader1': ('10.0.0.0', '23.0.0.0'),
+ 'worker1': ('10.0.0.2', None), 'worker3': ('10.0.0.4', None),
+ 'worker2': ('10.0.0.3', None), 'worker4': ('10.0.0.5', None)}
+ assert c.get_public_ip('leader1') == '23.0.0.0'
+ assert c.get_private_ip('leader1') == '10.0.0.0'
+ assert c.cluster_name == 'mycluster'
+ assert c.get_cluster_type() == 'azure'
+ assert c.version("accumulo").startswith('2.')
+ assert c.version("fluo").startswith('1.')
+ assert c.version("hadoop").startswith('3.')
+ assert c.version("zookeeper").startswith('3.')
+ assert c.get_service_private_ips("worker") == ['10.0.0.2', '10.0.0.3', '10.0.0.4', '10.0.0.5']
+ assert c.get('general', 'proxy_hostname') == "leader1"
+ assert c.proxy_public_ip() == "23.0.0.0"
+ assert c.proxy_private_ip() == "10.0.0.0"
+ assert c.get('general', 'cluster_user') == "centos"
+ assert c.get('general', 'cluster_group') == "centos"
+ assert c.get_non_proxy() == [('10.0.0.1', 'leader2'), ('10.0.0.2', 'worker1'), ('10.0.0.3', 'worker2'),
+ ('10.0.0.4', 'worker3'), ('10.0.0.5', 'worker4')]
+ assert c.get_host_services() == [('leader1', 'namenode resourcemanager accumulomaster zookeeper'),
+ ('leader2', 'metrics'),
+ ('worker1', 'worker swarmmanager'),
+ ('worker2', 'worker'),
+ ('worker3', 'worker'),
('worker4', 'worker')]