Monday, September 3, 2018

Ansible Test Driven Development with Molecule

Ansible Test Driven Development with Molecule

Molecule is a framework for doing TDD (Test Driven Development) for your Ansible roles. Using a variety of drivers, molecule lets you test your Ansible role on either Azure, Docker, Amazon EC2, Google Cloud, Linux Containers, Openstack or Vagrant. More information about how to use these drivers at https://molecule.readthedocs.io/en/latest/

In this post we will look at how we can use Docker locally for testing an Ansible role during development. If you want to test the commands given here, it is assumed that you have already installed Ansible, Docker, Python 2.7 and pip, and that you have a basic understanding on how to use them.

The source code for the role shown can be found at https://github.com/avnes/ansible-role-vscode and the role is used for installing Visual Studio Code on Linux.

Creating a Python virtual environment

Since both Ansible and molecule is written in Python, it is recommended to create a Python virtual environment where you can test your role. This also makes your tests more accessible for other developers if you document your PyPi packages in a requirements.txt file. I am a big fan of the virtualenvwrapper, which makes it a lot easier to work with multiple virtual environments:


mkvirtualenv moleculetdd
pip install docker-py==1.10.6
pip install molecule
molecule --version
ansible --version
# store it to a requirements.txt file:
pip freeze > requirements.txt

If you later need to return to this Python virtual environment, you can again use a feature from virtualennwrapper:

workon moleculetdd

Initializing a role

Normally you would use ansible-galaxy init <role name> to create a new Ansible role, but since we know we are going to use TDD with molecule, we can as well let molecule create the role structure for us too.

mkdir -p ~/git/roles
cd ~/git/roles
molecule init role --role-name ansible-role-vscode
# now you could do git init if this is a role you want
# to have under source control
cd ansible-role-vscode
ls -l


Personally I find myself using ansible galaxy init most of the time, and if you already have an Ansible role that just needs molecule tests, you would create the default test like this:

cd ~/git/roles/ansible-role-vscode
molecule init scenario --scenario-name default --role-name ansible-role-vscode
ls -l

The list of file and directories would look like this:

drwxrwxr-x. 2 <user> <user> 4096 <date and time> defaults
drwxrwxr-x. 2 <user> <user> 4096 <date and time> handlers
drwxrwxr-x. 2 <user> <user> 4096 <date and time> meta
drwxrwxr-x. 3 <user> <user> 4096 <date and time> molecule
-rw-r--r--. 1 <user> <user> 1330 <date and time> README.md
drwxrwxr-x. 2 <user> <user> 4096 <date and time> tasks
drwxrwxr-x. 2 <user> <user> 4096 <date and time> vars


We won't use handlers for this role, so I will remove that directory. It is also assumed that you write you own documentation, and places that in the README.md file using Markdown syntax.

In case it's been a while since you last created an Ansible role, or perhaps this is your first time, I will quickly go over what the purpose of the various directories are.

defaults is for storing variables with default values. These variables will be very easy to override, for instance through role inheritance, or if reading variables from an inventory. 

meta is, like the name suggest, for storing meta information about a role. This included the name of the author, a role description, supported platforms and dependencies on other roles.

molecule is for storing one to many test scenarios for the role, and this will be covered in great length later in this article. We have so far not specified which driver to use, but Docker is selected by default.

tasks is where your actual work is taking place. This of it as a place to keep one more related playbooks that together performs all the work the role is supposed to do.

vars is for (pretty static) variables that are harder to override. It might be good to study Ansible Variable Precedence to understand all the locations where Ansible looks for variables, and which precedence that takes effect if the same named variable is listed in several locations.

Inside the molecule's scenario directory, there is also a scenario specific documentation file, that is usually not used either, so I always delete it, and keep all my documentation in the README.md file as the role's root folder. So in this case, I would now run:

rm molecule/default/INSTALL.rst

For the sake of this article, we will only use molecule on a Fedora based testcase, so I have edited molecule/default/Dockerfile.j2 to install Python, Ansible and sudo inside a Fedora:26 docker image. Sudo is needed in the cases where you do become:yes in your role.

FROM {{ item.image }}

RUN dnf install -y python2 python2-dnf libselinux-python ansible sudo

CMD ["/bin/bash"]


So where is that {{ item.image }} defined? You will find it in molecule/default/molecule.yml:

---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
  enabled: False
platforms:
  - name: ansible-role-vscode-default
    image: fedora:26
provisioner:
  name: ansible
  lint:
    name: ansible-lint
scenario:
  name: default
verifier:
  name: testinfra
  lint:
    name: flake8


You will there see that the image has been configured to be fedora:26.

You are now able to test your Ansible role by running molecule --debug test
which will launch a Docker container based on Fedora 26, install Python, Ansible and sudo and finally running your role inside it.

At the time of writing this, Fedora 26 has already been superseeded by Fedora 27 and Fedora 28. Fedora 29 is scheduled to be launched in about 2 months.

This is where the power of molecule comes in. Just by changing the image line line in molecule.yml to use fedora:28, you have quickly made it possible to test on a later release in an isolated Docker environment. Great, isn't it!


Aborting, target uses selinux but python bindings (libselinux-python) aren't installed

The problem

When running Ansible in a Python virtual environment. Or when running molecule --debug test, you encounter the following error:

Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!

The investigation

There are a couple of possible root causes for this:

1. Maybe the error is right, and that libselinux-python is not installed. 
2. Python libraries for selinux are not available in a Python virtual environment.


The solution

To make sure libselinux-python is installed on Fedora:
sudo dnf install -y libselinux-python

To make sure libselinux-python is installed on CentOS/Fedora:
sudo yum install -y libselinux-python

Imaging you have a virtual environment called ansible26, you first need to activate that virtual environment and then copy the Python libraries for selinux over:

workon ansible26
cp -r /usr/lib64/python2.7/site-packages/selinux $VIRTUAL_ENVv/lib/python2.7/site-packages
cp /usr/lib64/python2.7/site-packages/_selinux.so $VIRTUAL_ENVv/lib/python2.7/site-packages

Please note that workon is a command from https://virtualenvwrapper.readthedocs.io/ and if you haven't installed virtualenvwrapper, you would rather navigate to your virtual environment directory, and then run source bin/activate

Credits to https://github.com/metacloud/molecule/issues/1209 where I found this solution.

Monday, April 23, 2018

Using Azure Service Principal login with Ansible

The problem

When using the Azure REST API from Ansible using the uri module you need to ensure you are authenticated towards Azure. The easiest way to do that is to set a Bearer token based on a Service Principal user on Azure. 

This is the official Azure REST API documentation: https://docs.microsoft.com/en-us/rest/api/azure/

This is how you can create a service principal:

When you have created an Azure service principal you will have 4 necessary pieces of information:

  • Tenant id (the protected resource)
  • Client id (username)
  • Client secret (password)
  • Subscription id (you already had that from your user profile)
Make sure to save those values to an Ansible Vault. In my example I have called them:

  • vault_az_tenant_id
  • vault_az_client_id
  • vault_az_client_secret
  • vault_az_subscription_id

The solution

Create a playbook.yml with the following information:

---
- hosts: localhost
  vars:
    az_tenant_id: "{{ vault_az_tenant_id }}"
    az_client_id: "{{ vault_az_client_id }}"
    az_client_secret: "{{ vault_az_client_secret }}"
    az_subscription_id: "{{ vault_az_subscription_id }}"
    az_token_url: "https://login.microsoftonline.com/{{ az_tenant_id }}/oauth2/token"
    az_token_body: >
        resource=https://management.core.windows.net/
        &client_id={{ az_client_id }}
        &grant_type=client_credentials
        &client_secret={{ az_client_secret }}

  tasks:
    - name: Login to Azure
      uri:
        url: "{{ az_token_url }}"
        method: POST
        body: "{{ az_token_body }}"
        headers:
          Content-Type: "application/x-www-form-urlencoded"
        status_code: 200
      register: login

    - name: Setting Bearer token as fact
      set_fact:
        az_bearer: "{{ login.json.access_token }}"
 

That is how easy it is to authenticate with Azure from the Ansible uri module. Thanks to Vivek to helping out with the az_token_body.

Now every time you us the Ansible uri module in the same playbook, you just need to add the Bearer token to the request header, like this:

- uri:
        url: "{{ whatever-azure-url }}"
        method: POST
        headers:
          Authorization: "Bearer {{ az_bearer }}"

Wednesday, August 16, 2017

False positive: ansible-lint reports [ANSIBLE0002] Trailing whitespace when there are none

The problem

When running ansible-lint on a Ansible role, it reported [ANSIBLE0002] Trailing whitespace on every line in one of my task files, like shown in the example below:


(vansible23)[audun@hostname my-role]$ ansible-lint .
[ANSIBLE0002] Trailing whitespace
/home/audun/git/my-role/tasks/main.yml:1
---

[ANSIBLE0002] Trailing whitespace
/home/audun/git/my-role/tasks/main.yml:2


[ANSIBLE0002] Trailing whitespace
/home/audun/git/my-role/tasks/main.yml:3
- debug: msg="Hello World"



The investigation

I had a suspicion there might be hidden characters in the file, but before running it through a HEX editor, I tried to check it with the Linux file command:


(vansible23)[audun@hostname my-role]$ file /home/audun/git/my-role/tasks/main.yml
/home/audun/git/my-role/tasks/main.yml: ASCII text, with CRLF line terminators



As shown above, it turned out that the file had CRLF line terminators on every line, indicating that this file had been created on Windows. That was luckily easy to fix.


The solution

The dos2unix command was used to change the file to Unix format:


(vansible23)[audun@hostname my-role]$ dos2unix /home/audun/git/my-role/tasks/main.yml
dos2unix: converting file /home/audun/git/my-role/tasks/main.yml to Unix format ...



Afterwards ansible-lint reported no issues:


(vansible23)[audun@hostname my-role]$ ansible-lint .

Wednesday, March 15, 2017

Using atom-beautify package to beautify a Markdown file results in: Error: Cannot find module '../lib/language-code-rewrites'

The problem

I was using Atom Editor 1.14.4 with atom-beautify 0.29.17 package. When I tried to beautify a README.md file created with ansible-galaxy init <rolename> I got the following exception:

File 0Project 0No IssuesREADME.md9:77
LFUTF-8GitHub Markdowngit+
Cannot find module '../lib/language-code-rewrites'
Cannot find module '../lib/language-code-rewrites'
Hide Stack Trace
Error: Cannot find module '../lib/language-code-rewrites'
    at Module._resolveFilename (module.js:455:15)
    at Module._resolveFilename (/usr/share/atom/resources/electron.asar/common/reset-search-paths.js:35:12)
    at Function.Module._resolveFilename (/usr/share/atom/resources/app.asar/src/module-cache.js:383:52)
    at Function.Module._load (module.js:403:25)
    at Module.require (module.js:483:17)
    at require (/usr/share/atom/resources/app.asar/src/native-compile-cache.js:50:27)
    at Object.<anonymous> (/home/ane058/.atom/packages/atom-beautify/node_modules/tidy-markdown/lib/converters.js:12:23)
    at Module._compile (/usr/share/atom/resources/app.asar/src/native-compile-cache.js:109:30)
    at Object.value [as .js] (/usr/share/atom/resources/app.asar/src/compile-cache.js:216:21)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.require (module.js:483:17)
    at require (/usr/share/atom/resources/app.asar/src/native-compile-cache.js:50:27)
    at Object.<anonymous> (/home/ane058/.atom/packages/atom-beautify/node_modules/tidy-markdown/lib/index.js:16:14)
    at Module._compile (/usr/share/atom/resources/app.asar/src/native-compile-cache.js:109:30)
    at Object.value [as .js] (/usr/share/atom/resources/app.asar/src/compile-cache.js:216:21)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.require (module.js:483:17)
    at require (/usr/share/atom/resources/app.asar/src/native-compile-cache.js:50:27)
    at /home/ane058/.atom/packages/atom-beautify/src/beautifiers/tidy-markdown.coffee:13:22
    at Promise._execute (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/debuggability.js:300:9)
    at Promise._resolveFromExecutor (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:483:18)
    at new Promise (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:79:10)
    at TidyMarkdown.module.exports.TidyMarkdown.beautify (/home/ane058/.atom/packages/atom-beautify/src/beautifiers/tidy-markdown.coffee:12:16)
    at /home/ane058/.atom/packages/atom-beautify/src/beautifiers/index.coffee:318:24
    at Promise._execute (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/debuggability.js:300:9)
    at Promise._resolveFromExecutor (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:483:18)
    at new Promise (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:79:10)
    at /home/ane058/.atom/packages/atom-beautify/src/beautifiers/index.coffee:240:18
    at tryCatcher (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:693:18)
    at Promise._fulfill (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:638:18)
    at PromiseArray._resolve (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise_array.js:126:19)
    at PromiseArray._promiseFulfilled (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise_array.js:144:14)
    at Promise._settlePromise (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:574:26)
    at Promise._settlePromise0 (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:693:18)
    at Promise._fulfill (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:638:18)
    at Promise._resolveCallback (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:432:57)
    at Promise._settlePromiseFromHandler (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:524:17)
    at Promise._settlePromise (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:693:18)
    at Promise._fulfill (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:638:18)
    at Promise._resolveCallback (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:432:57)
    at Promise._settlePromiseFromHandler (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:524:17)
    at Promise._settlePromise (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:693:18)
    at Promise._fulfill (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:638:18)
    at Promise._resolveCallback (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:432:57)
    at ReductionPromiseArray._resolve (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/reduce.js:61:19)
    at Promise.completed [as _fulfillmentHandler0] (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/reduce.js:122:15)
    at Promise._settlePromise (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:566:21)
    at Promise._settlePromise0 (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/promise.js:693:18)
    at Async._drainQueue (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/async.js:133:16)
    at Async._drainQueues (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/async.js:143:10)
    at Async.drainQueues (/home/ane058/.atom/packages/atom-beautify/node_modules/bluebird/js/release/async.js:17:14)

I logged it as an issue at https://github.com/Glavin001/atom-beautify/issues/1549, but after a few days of tinkering I finally found the likely root cause, and working solution.

The investigation

The exception suggest there is an issue with the tidy-markdown dependency, and it turned out that tidy-markdown 2.0.5 npm package had been released a few days earlier at https://www.npmjs.com/package/tidy-markdown 

ls ~/.atom/.apm/tidy-markdown/
showed that atom-beautify had pulled down the tidy-markdown 2.0.5 package.

The solution

cd ~/.atom/packages/atom-beautify
npm install tidy-markdown@2.0.4


Saturday, January 21, 2017

Released my second role to Ansible Galaxy today

The ansible-role-tint2 is an Ansible role for installing the tint2 panel for Linux. Currently the role supports the following platforms (in alphabetic order):


  • Arch Linux
  • Debian sid
  • Debian stretch
  • Fedora 24
  • Fedora 25 
  • Ubuntu 16.04
  • Ubuntu 16.10

The Ansible role can be found here: https://github.com/avnes/ansible-role-tint2
It has been released under the liberal MIT license.

While this Ansible role and this blog post of somewhat similar to my previous Ansible role, I used a much more flexible and portable test framework based on mulecule.

Saturday, January 14, 2017

Released my first role to Ansible Galaxy today

The ansible-role-conky is an Ansible role for installing the conky monitoring overlay tool. Currently the role supports the following platforms (in alphabetic order):


  • Arch Linux
  • Debian sid
  • Debian stretch
  • Fedora 24
  • Fedora 25 
  • Ubuntu 16.04
  • Ubuntu 16.10

The Ansible role can be found here: https://galaxy.ansible.com/avnes/ansible-role-conky/
It has been released under the liberal MIT license.

Wednesday, November 23, 2016

Customize Fedora 25 in a few minutes using Ansible

Instead of spending a few hours after installing Fedora 25 on adding the software you want and tweaking the options you prefer, why not use Ansible to get you up and running in minutes?

I assume you have already downloaded Fedora 25 Workstation from the official site at https://getfedora.org/, and that you have installed it, and that your personal user is allowed to run sudo (hint: being a member of the wheel group).


After logon to Fedora 25, download my Ansible playbook sample from https://github.com/avnes/blogsamples/blob/master/fedora.yml 

The rest of this article assumes you saved the file to ~/fedora.yml

Open ~/fedora.yml in a text editor (for instance vi or Gedit) and have look at what software it is installing, and what software it removes. Change it if you like.

When you are ready to play, run the following commands as your personal user:
sudo dnf install -y ansible
ansible-playbook ~/fedora.yml --connection=local --ask-become-pass

When promoted for a password, type in your personal password, and the playbook will then be executed with sudo rights. Both the sudo command and the ansible.playbook command will prompt you for your personal password.

Afterwards you should see a similar output as below, though changed would be higher than 0. In my case of is 0 because I have already run the playbook a few times already, so no changes.




Wednesday, November 9, 2016

How to install Vagrant with Oracle® VM VirtualBox and libvirt support on Antergos Linux

Installation

I recently needed to install Vagrant with support for Oracle® VM VirtualBox and libvirt for my dixie project at GitHub. Some of the virtualbox dependencies was found in this excellent post over at forum.antergos.com. Then I wrapped it into a script, and the here is the part of the script that installs and configures it:

if [ $(uname -r | grep ARCH | wc -l) -gt 0 ]; then
  sudo pacman -S --needed --noconfirm vagrant
  sudo pacman -S --needed --noconfirm libvirt
  sudo pacman -S --needed --noconfirm linux-headers
  sudo pacman -S --needed --noconfirm virtualbox virtualbox-guest-iso
  sudo pacman -S --needed --noconfirm vde2 net-tools virtualbox-ext-vnc virtualbox-host-modules-arch
  sudo pacman -S --needed --noconfirm ansible rsync
  sudo su -c "modprobe vboxdrv" || echo "Reboot your computer and try again"
  if [ ! -f "/etc/modules-load.d/virtualbox.conf" ]; then
    sudo touch /etc/modules-load.d/virtualbox.conf
  fi

  if [ $(grep vboxdrv /etc/modules-load.d/virtualbox.conf | wc -l) -eq 0 ]; then
    sudo su -c 'echo "vboxdrv" >> /etc/modules-load.d/virtualbox.conf'
  fi

  if [ $(grep vboxnetadp /etc/modules-load.d/virtualbox.conf | wc -l) -eq 0 ]; then
    sudo su -c 'echo "vboxnetadp" >> /etc/modules-load.d/virtualbox.conf'
  fi
  if [ $(grep vboxnetflt /etc/modules-load.d/virtualbox.conf | wc -l) -eq 0 ]; then
    sudo su -c 'echo "vboxnetflt" >> /etc/modules-load.d/virtualbox.conf'
  fi
  if [ $(grep vboxpci /etc/modules-load.d/virtualbox.conf | wc -l) -eq 0 ]; then
    sudo su -c 'echo "vboxpci" >> /etc/modules-load.d/virtualbox.conf'
  fi
fi

Install Ruby with gem support on Red Hat® Enterprise Linux

This post is heavily influenced by http://collectiveidea.com/blog/archives/2011/10/31/install-ruby-193-with-libyaml-on-centos/, so all credit to that author.

The problem

Installing a Ruby gem with gem install on  Red Hat® Enterprise Linux 6.8 returns errors indicating problems with libyaml:

gem install bundler
/usr/local/lib/ruby/1.9.1/yaml.rb:84:in `<top (required)>':
It seems your ruby installation is missing psych (for YAML output).
To eliminate this warning, please install libyaml and reinstall your ruby.

The investigation

Trying to reinstall libyaml and libyaml-devel followed by a reinstall of ruby did not solve the issue.

The solution

After investigating this issue for a while, and not able to solve it through yum, I considered to either install and use rvm for this, or to download and compile the source. I found  http://collectiveidea.com/blog/archives/2011/10/31/install-ruby-193-with-libyaml-on-centos/ where the author had good success with the latter, so I did the same, but cleaning up all by Ruby and libyaml stuff from yum first:

yum remove -y ruby libyaml libyaml-devel rubygem-rake rubygems rubygems-devel

# Download and install libyaml
cd /tmp
wget http://pyyaml.org/download/libyaml/yaml-0.1.7.tar.gz
tar xzvf yaml-0.1.7.tar.gz
cd yaml-0.1.7
./configure --prefix=/usr/local
make
make install

# Download and install Ruby
cd /tmp
wget http://ftp.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
tar xzvf ruby-2.3.1.tar.gz
cd ruby-2.3.1
./configure --prefix=/usr/local --enable-shared --disable-install-doc --with-opt-dir=/usr/local/lib
make install

# Install your gems
gem install bundler

Monday, October 10, 2016

Oracle® XE Configuration Assistant hangs on Fedora Linux in VirtualBox 5

The problem

I was installing Oracle® XE on Fedora Linux 23 on Oracle® VirtualBox. I noticed that the database configuration assistant was just hanging forever on my desktop PC with an AMD CPU. I was not able to reproduce the same issue on a desktop PC with an Intel CPU.

The cause

I have previously encountered a similar problem with Oracle® 12c Database Configuration Assistant that hangs on Oracle® Linux 7 in VirtualBox 5, and it turned out that the solution was the same for this issue.

The solution

I found the culprit to be the Paravirtualization Interface for acceleration. Changing that from Default to None fixed the issue.



The above screen dumps were made on a virtual session for Centos 7, but it is the same workaround for Fedora Linux and Oracle® Linux running in VirtualBox too.

Friday, September 9, 2016

Development In a X Integrated Environment

It's been almost 7.5 years since I last released anything through Open Source development. At that time I was using SourceForge and storing the source code in CVS.

Today I made a comeback. I started a project on GitHub called dixie, which is short for Development In a X Integrated Environment.

It is partially to have a practical task for learning Vagrant, Ansible and Kickstart, but in the end I think it can result in a pretty usable product too.

Using a Windows server as slave node for Hudson

Installed Cygwin64, and on the Select Packages screen I manually had to choose to install openssh and cygrunsrv as they are not picked by default. 

Created local Windows user sshd on SLAVESRV1. Unchecked "Change password at first login". Checked "Password never expires".

Created local Windows user cyg_server on SLAVESRV1. Unchecked "Change password at first login". Checked "Password never expires".

Added local Windows user cyg_server to local Administrators group.

Created local Windows user hudson on SLAVESRV1. Unchecked "Change password at first login". Checked "Password never expires".

Added local hudson Windows user to local 'Remote Desktop Users' group.

Started "Cygwin64 Terminal" with "Run as Administrator"

Initially I had some issue with "/bin/bash: Operation not permitted", but then I found https://cygwin.com/ml/cygwin/2016-03/msg00097.html with a solution:
Edited the file /bin/ssh-host-config, and after the line CSIH_SCRIPT=/usr/share/csih/cygwin-service-installation-helper.sh I added the following:
editrights -a SeAssignPrimaryTokenPrivilege -u cyg_server
editrights -a SeCreateTokenPrivilege -u cyg_server
editrights -a SeTcbPrivilege -u cyg_server
editrights -a SeServiceLogonRight -u cyg_server

Ran the ssh-host-config script
*** Query: you have the required privileges) (yes/no) yes
*** Query: Should StrictModes be used? (yes/no) no
*** Query: Should privilege separation be used? (yes/no) yes
*** Query: (Say "no" if it is already installed as a service) (yes/no) yes
*** Query: Enter the value of CYGWIN for the daemon: [] binmode ntsec
*** Info: 'SLAVESRV1+cyg_server' will only be used by registered services.
*** Query: Do you want to use a different name? (yes/no) no
*** Query: Please enter the password for user 'SLAVESRV1+cyg_server':

Started Windows service 'CYGWIN sshd'

Verified that SSH was started on port 22 with: netstat -an | grep LISTEN

Logged on to Windows as hudson through Remote Desktop

As hudson user started Cygwin. This ensured /home/hudson folder was created.

Now I was finally ready to add the SLAVESRV1 as a node from the Hudson master using the SLAVESRV1+hudson user for SSH connections. It was colleague that discovered that we needed to prefix the user with SLAVESRV1+ since it was a local user and not a domain user.

Tuesday, August 2, 2016

Remote deployment failed (oracle.jdevimpl.deploy.common.Jsr88RemoteDeployer) with Oracle® JDeveloper

The problem

Using JDeveloper 12.1.3, I had created a dummy BPMN Process with just a User Task bound to a human task. For this task I had also auto-generated an input form. Deploying the task form went fine, but when trying to deploy the BPMN process I got the following error:


[08:30:27 AM] Deployment cancelled.
[08:30:27 AM] Taskflow deployment failed to deploy to server. Remote deployment failed
[08:30:27 AM] Deployment cancelled.
[08:30:27 AM] ----  Deployment incomplete  ----.
[08:30:27 AM] Remote deployment failed (oracle.jdevimpl.deploy.common.Jsr88RemoteDeployer)

The cause

After trying various things, I discovered the problem was my proxy settings in Oracle® JDeveloper.

The solution

Tools -> Preferences -> Web Browser and Proxy -> Proxy Settings, and then I set it to No Proxy. 
Now I could deploy the process without any errors. 

I assume it would also have worked if I had set it to Manual Proxy Settings, and then added my hostname to the exception list.

Wednesday, June 29, 2016

The art of creating efficient algorithms

How often do we think outside the box to create a creative and efficient algorithm when we develop software? I bet answer is "probably not often enough". I have not been coding in Python for close to two years. In fact I have not been coding much for the past two years in any language apart from a few Java utilities and the odd shell scripts to help me get things done in a predicable and automated manner. Hence this is not a post about how to create good algorithms, but rather some reflections on code performance during my re-entry to Python programming. So please forgive me for any false assumptions and poor code quality.

While reading a book on Python 3 recently, I came across an interesting anecdote about the German mathematician Carl Friedrich Gauss, who at the age of 8 was given a homework assignment to sum up all the numbers between 1 and 100. To his teacher’s astonishment Carl Friedrich answered 5050 a few seconds later, and it was the teacher had to go home and check if the answer was correct.

Instead of adding every number between 1 and 100, the young Carl Friedrich quickly realized that there were 50 pairs of unequal numbers that would sum up to 100:
99 + 1
98 + 2
And so on, all the way down to 51 + 49
And since he had not included the number 50 yet, the answer was (50 pairs * 100) + 50 = 5050

So the youngster had by himself discovered a pretty efficient algorithm for summing up integers. Not bad for an 8 year old! According to Wikipedia this story is contested, but nevertheless it is a very good example of a creative solution for optimizing an algorithm.

Naturally I needed to test the same logic on a computer, so I wrote a small Python script to check the human way of thinking (the count()method) and the young Carl Friedrich' way of thinking (the f_count() method):

import time

class FunnyMath(object):

    def __init__(self,num=0):
        self.num = num
   
    def count(self,num=0):
        start = time.time()
        self.num = num;
        for i in range(1,num):
            self.num += i
        end = time.time()
        print("Traditional approach took {} seconds to get the result: {}".format(round((end-start),2),int(self.num)))
    
    def f_count(self,num=0):
        start = time.time()
        self.num = num**2/2.0 + num/2.0
        end = time.time()
        print("Creative approach took {} seconds to get the result: {}".format(round((end-start),2),int(self.num)))
    
def main():
    fm = FunnyMath()
    num = 100000000
    fm.f_count(num)
    fm.count(num)
   
if __name__ == "__main__":
    main()

How long it takes to run will of course depend on your hardware resources. I tested this script on two different laptops.

On the "fast" laptop I got the following output:
Creative approach took 0.0 seconds to get the result: 5000000050000000
Traditional approach took 14.88 seconds to get the result: 5000000050000000

On the "slow" laptop I got the following output:
Creative approach took 0.0 seconds to get the result: 5000000050000000
Traditional approach took 27.76 seconds to get the result: 5000000050000000

So it is an interesting example that shows that some algorithms can and should be optimized. Not only did f_count() perform better. It also had fewer lines of code.

 But with a rewrite I could also optimize the slow count() method:

import time

class FunnyMath(object):

    def __init__(self,num=0):
        assert type(num) is int, "num was not an integer {}".format(num)
        self.num = num
   
    def count(self):
        num = self.num
        for i in range(1,num):
            num += i
        return int(num)
        
    def f_count(self):
        return int(self.num**2/2.0 + self.num/2.0)
    
def main():
    fm = FunnyMath(num = 100000000)
    start = time.time() 
    num = fm.f_count()
    end = time.time()
    print("Creative approach took {} seconds to get the result: {}".format(round((end-start),2),num))
    
    start = time.time()
    num = fm.count()
    end = time.time()
    print("Traditional approach took {} seconds to get the result: {}".format(round((end-start),2),num))
    
if __name__ == "__main__":
    main()

Then I ran it on the "fast" laptop and got the following output:
Creative approach took 0.0 seconds to get the result: 5000000050000000
Traditional approach took 5.98 seconds to get the result: 5000000050000000

A full rewrite was not really necessary, but I wanted a cleaner and more compact code for my class. The observant reader might have spotted the real problem with the first definition of the count() method:

def count(self,num=0):
        start = time.time()
        self.num = num;
        for i in range(1,num):
            self.num += i
        end = time.time()
        print("Traditional approach took {} seconds to get the result: {}".format(round((end-start),2),int(self.num)))
 
For each iteration in the loop I was updating the class variable num instead of just using a local variable called num, so the same performance gain for the count() method could have been achieved with a small rewrite:

def count(self,num=0):
        start = time.time()
        num = num;
        for i in range(1,num):
            num += i
        end = time.time()
        print("Traditional approach took {} seconds to get the result: {}".format(round((end-start),2),int(num)))

When I ran that on the "fast" laptop I got the the following output:
Traditional approach took 5.92 seconds to get the result: 5000000050000000

The reason for this performance gain is explained in the Python wiki.

Still, the count() method would always be slower than the f_count() method.

Wednesday, May 11, 2016

weblogic.store.PersistentStoreFatalException: [Store:280019]There was an error while writing to a storage

The problem

We discovered that an Oracle® WebLogic managed server was in status FAILED on a development environment. The apparent reason for this was the following error:

weblogic.store.PersistentStoreFatalException: [Store:280019]There was an error while writing to a storage

The cause

The can be multiple reasons or the above error, but the logical guess was that our /data mount point had run out of disk space, because that mount point was where our domain configuration (and persistent stores) were saved. That theory turned out to be wrong. The /data mount point was only 12% utilized on a 100GB partition, so there was plenty of available disk.

Then I tried to create an empty file on the mount point:

# touch /data/dummy
# touch: cannot touch `/data/dummy': Read-only file system


When checking the /var/log/messages file, I found the reason for that:

May 11 07:37:45 <hostname> kernel: JBD2: I/O error detected when updating journal superblock for dm-25-8.
May 11 07:37:45 <hostname> kernel: EXT4-fs error (device dm-25): ext4_journal_start_sb: Detected aborted journal
May 11 07:37:45 <hostname> kernel: EXT4-fs (dm-25): Remounting filesystem read-only

Comparing the commands cat /etc/fstab and ls -l /dev/mapper/ | grep dm-25 also confirmed that device dm-25 was indeed the source for the /data mount point.

The solution

After shutting down everything as cleanly as I could, I checked if there were still processes trying to use the /data mount point:

lsof | grep "/data"

It turned out that there was a few processes I was unable to shutdown cleanly, so I had to kill those. 

Then I unmounted the /data mount point:

umount /data


Finally I fixed the issue by running fsck and mounting the partition again. 

Caution: Running fsck on a partition can in worst case scenario be destructive, so make sure you have a valid backup!

fsck /data
mount /data

Now I could start up Oracle® WebLogic Server without any issues.

Thursday, April 28, 2016

RDA-10913: Unknown profile "FM12c_SoaMin" when running Remote Diagnostic Agent on Oracle® SOA Suite 12.1.3

The problem

You are trying to collect information about the Oracle® SOA Suite 12.1.3 using the Remote Diagnostic Agent (RDA), but it fails with a RDA-10913: Unknown profile "FM12c_SoaMin" error, like shown below:

source $DOMAIN_HOME/bin/setDomainEnv.sh
cd $SOA_ORACLE_HOME
cd ../oracle_common/rda
./rda.sh -s soa_issue -p FM12c_SoaMin

RDA-00013: Error reported by the command "setup":
 RDA-00014: Request error "Setup":
  RDA-10913: Unknown profile "FM12c_SoaMin"

The cause

There is something wrong with the RDA supplied with  Oracle® SOA Suite 12.1.3, and this is how I discovered that:

The command ./rda.sh -V told me I was running RDA 8.02, which looked fine.

By reading the full output from ./rda.sh -V I could also see that the required modules where installed, according to Oracle Support Doc ID 391983.1.

Using ./rda.sh -h told me which options RDA is accepting, And in particular I noted the -L flag:
-L     List the available modules, profiles, and conversion groups

./rda.sh -L | grep Soa
  FM11g_Soa                        Oracle SOA Suite 11g problems
  FM11g_SoaMin                     Oracle SOA Suite 11g problems (minimum)

As you can see, the RDA shipped with Oracle® SOA Suite 12.1.3 was only supporting Oracle® SOA Suite 11g diagnostics.

The solution

  1. Downloaded the latest version of the RDA tool for Oracle® Fusion Middleware. At the time of writing this, the patch number was 22783073.
  2. Used opatch apply to install the patch I just downloaded.
  3. Verified which SOA profiles were now available by running:
    ./rda.sh -L | grep Soa
    OFM_SoaMax                       OFM SOA Suite problems (11g/12c+) : full
    OFM_SoaMin                       OFM SOA Suite problems (11g/12c+) : minimum

     
  4. Ran the RDA with the new module for Oracle® SOA Suite 11g and 12c:
    ./rda.sh -s soa_issue -p OFM_SoaMin

Monday, March 14, 2016

Oracle® WebLogic managed server reaches ADMIN state during startup due to name collision

The problem

When starting a Oracle® WebLogic managed server it halts when reaching the ADMIN state. 
Looking at the managed server's out file I saw the following error:

<Mar 15, 2016 8:45:40 AM UTC> <Error> <Deployer> <BEA-149231> <Unable to set the activation state to true for the application "MyDataSource".
weblogic.application.ModuleException: weblogic.common.ResourceException: Failed to bind remote object (ClusterableRemoteRef(6058417782957188722S:my-server:soa_domain:soa_server1 null)/289        [weblogic.jdbc.common.internal.RemoteDataSource]) to replica aware stub at MY_DS(ClusterableRemoteRef(6058417782957188722S:my-server:soa_domain:soa_server1 [6058417782957188722S:my-server:soa_domain:soa_server1/288])/288        [weblogic.jdbc.common.internal.RemoteDataSource])
        at weblogic.jdbc.module.JDBCModule.activate(JDBCModule.java:411)
        at weblogic.application.internal.flow.ModuleListenerInvoker.activate(ModuleListenerInvoker.java:114)
        at weblogic.application.internal.flow.ModuleStateDriver$2.next(ModuleStateDriver.java:195)
        at weblogic.application.internal.flow.ModuleStateDriver$2.next(ModuleStateDriver.java:190)
        at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:42)
        Truncated. see log file for complete stacktrace
Caused By: weblogic.common.ResourceException: Failed to bind remote object (ClusterableRemoteRef(6058417782957188722S:my-server:soa_domain:soa_server1 null)/289   [weblogic.jdbc.common.internal.RemoteDataSource]) to replica aware stub at MY_DS(ClusterableRemoteRef(6058417782957188722S:my-server:


The cause

The error above says I have a problem starting the MyDataSource data source due to a name collision. Logging into the WebLogic console, and looking at my data sources, I realized I had two data sources with the same JNDI location. The reason was that I had prepared a new data source long time ago, and forgotten all about it. During the startup of the managed server, my new data source had started up, but the old one could of course not start since it had the same JNDI  location.

The solution

  1. Removed the old and obsolete data source with the conflicting JNDI location.
  2. Checked the managed server's out file for other errors, and there were none. 
  3. Resumed startup of the managed server so the server state changed from ADMIN to RUNNING. 



Thursday, March 10, 2016

Oracle® SOA Suite 12.1.3 startup fails with CoherenceException due to ensureWKAAddresses

The problem

After a fresh install of Oracle® SOA Suite 12.1.3 with BPM and BAM enabled, both the SOA server and the BAM server failed to startup due to the following exception:

<Mar 9, 2016 9:49:34 AM UTC> <Error> <weblogic-coherence-integration> <BEA-000012> <Server bam_server1 is configured with localhost as the Unicast Listen Address which is an error in Coherence Production mode. A generated Coherence WKA list will not operate correctly across multiple machines.>
Mar 09, 2016 9:49:36 AM oracle.dms.servlet.DMSServletFilter setEagerlySetContextValues
INFO: The setting that controls the eager fetching of some types of execution context data has been set to true.
<Mar 9, 2016 9:49:37 AM UTC> <Critical> <WebLogicServer> <BEA-000362> <Server failed. Reason:

There are 1 nested errors:

weblogic.cacheprovider.coherence.CoherenceException:
    at weblogic.cacheprovider.coherence.CoherenceClusterManager.ensureWKAAddresses(CoherenceClusterManager.java:510)
    at weblogic.cacheprovider.coherence.CoherenceClusterManager.configureClusterService(CoherenceClusterManager.java:236)
    at weblogic.cacheprovider.CacheProviderServerService.bootCoherenceFromWLSCluster(CacheProviderServerService.java:225)
    at weblogic.cacheprovider.CacheProviderServerService.initCoherence(CacheProviderServerService.java:94)
    at weblogic.cacheprovider.CacheProviderServerService.initialize(CacheProviderServerService.java:71)
    at weblogic.cacheprovider.CacheProviderServerService.start(CacheProviderServerService.java:65)
    at weblogic.server.AbstractServerService.postConstruct(AbstractServerService.java:78)
    at sun.reflect.GeneratedMethodAccessor34.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.glassfish.hk2.utilities.reflection.ReflectionHelper.invoke(ReflectionHelper.java:1017)
    at org.jvnet.hk2.internal.ClazzCreator.postConstructMe(ClazzCreator.java:388)
    at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:430)
    at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:456)
    at org.glassfish.hk2.runlevel.internal.AsyncRunLevelContext.findOrCreate(AsyncRunLevelContext.java:225)
    at org.glassfish.hk2.runlevel.RunLevelContext.findOrCreate(RunLevelContext.java:82)
    at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2488)
    at org.jvnet.hk2.internal.ServiceHandleImpl.getService(ServiceHandleImpl.java:98)
    at org.jvnet.hk2.internal.ServiceHandleImpl.getService(ServiceHandleImpl.java:87)
    at org.glassfish.hk2.runlevel.internal.CurrentTaskFuture$QueueRunner.oneJob(CurrentTaskFuture.java:1162)
    at org.glassfish.hk2.runlevel.internal.CurrentTaskFuture$QueueRunner.run(CurrentTaskFuture.java:1147)
    at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:548)
    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:311)
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:263)

>
<Mar 9, 2016 9:49:37 AM UTC> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to FAILED.>
<Mar 9, 2016 9:49:37 AM UTC> <Error> <WebLogicServer> <BEA-000383> <A critical service failed. The server will shut itself down.>
<Mar 9, 2016 9:49:37 AM UTC> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to FORCE_SHUTTING_DOWN.>

The cause

As stated in the exception, the issue is that the servers are listening on localhost instead of the actual hostname while the Oracle® WebLogic Server is running in Production mode.

The solution

There are multiple solutions to this one, but the two most obvious ones are to either run Oracle® WebLogic Server is running in Development mode, or to listen to the actual hostname instead of localhost. I would not recommend the former. Not unless it is a single user development system.

Run the hostname command to find the actual hostname. The hostname command works on both Linux, Unix and Windows.
Edit the managed servers through the  Oracle® WebLogic Server console, and set the value returned from the hostname command as the Listen Address,

Tuesday, March 1, 2016

Could not initialize class sun.awt.X11GraphicsEnvironment for frevvo module in Oracle® BPM Suite 12.2.1

The problem

BPM Composer in Oracle® BPM Suite 12.2.1 fails when open enterprise maps, and the SOA server's out file says:

<Mar 1, 2016 7:31:43 AM UTC> <Error> <HTTP> <BEA-101017> <[ServletContext@881844748[app:frevvo module:/frevvo path:null spec-version:3.1]] Root cause of ServletException.
java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11GraphicsEnvironment
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at java.awt.GraphicsEnvironment.createGE(GraphicsEnvironment.java:103)
    at java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:82)
    at sun.awt.X11FontManager.isHeadless(X11FontManager.java:511)
    Truncated. see log file for complete stacktrace
>

The cause

The JVM was not running headless, and it did not have a DISPLAY variable set to point to a X server.

The solution

Added -Djava.awt.headless=true to the soa server, and restarted it.