Oracle VM for x86: Hard Partitioning Hands On

As most of you likely know, Oracle has stringent licensing rules when it comes to running their software in a virtual environment.  With anything other than Oracle VM Server for x86, you basically have to license every core in the cluster (VMware, Hyper-V, etc).  With OVM, Oracle does accept a specific configuration that satisfies their definition of a “hard partition” where processor licensing is concerned.  This means that if you own 2 processor licenses for Oracle Database EE for example, and are running on a platform that has a .5 license multiplier (such as x86), you are entitled to run that software on 4 cores.


Here are the requirements to satisfy the hard partition I mentioned above (taken from a document that is linked in InfoDoc 1529408.1):

To conform to the Oracle hard partition licensing requirement, you must follow the instructions described in this white paper to bind vCPUs to physical CPU threads or cores.


p style=”padding-left:30px;”>Live migration of CPU pinned virtual machines to another Oracle VM Server is not permitted under the terms of the hard partitioning license. Consequently, for Oracle VM Release 3, any servers running CPU pinned guests must not be included in DRS (Distributed Resource Scheduler) and DPM (Distributed Power Management) policies.
When live migration is used in an Oracle VM server pool, hard partition licensing is not applicable. You must determine the number of virtual machines running the Oracle Software and then license the same number of physical servers (starting with the largest servers based on the CPU core count) up to the total number of the physical servers in the pool. For example, if a customer has a server pool with 32 servers and 20 virtual machines running Oracle Software within the server pool, the customer must license the 20 largest physical servers in the pool. If the customer is running 50 virtual machines with Oracle Software in a pool of 32 physical servers, they need only to license the 32 physical servers in the pool.

Live migration of other virtual machines with non-Oracle software within the server pool is not relevant to Oracle software hard partitioning or has no impact to how Oracle software license is calculated.

“Trusted Partitions” allow subset licensing without limitation on live migration, but only available on the approved Oracle Engineered Systems listed on Oracle licensing policies for partitioned environments.


There is more information in that document on how to actually perform the CPU pinning but we don’t need to get into that level of detail just yet.  To summarize- here are the key takeaways you should be aware of when considering using OVM for hard partitioning:

  • The use of hyperthreading or no hyperthreading is irrelevant to Oracle from a licensing perspective
  • vCPUs are bound or “pinned” to physical cores using an OVM Manager utility that must be downloaded and installed on your OVM Manager
  • Live Migration, DRS and DPM is not allowed for pinned VMs
  • You have to choose which vCPUs you want to PIN your VM to.  Be careful that you don’t accidentally pin more than one VM to a given set of vCPUs- it’s a completely valid configuration but your performance will go to hell due to contention in the CPU scheduler.
  • Get in the habit of pinning your secondary workloads (applications that don’t require hard partitions) to a set of unused vCPUs.  This way they can’t potentially run on the same vCPU that you just pinned your production database VM to.
  • Make sure when you bind vCPUs that you don’t accidentally cross core boundaries.  It only takes 1 vCPU running on a separate core to mess up your licensing costs.  See my blog post here to get an idea of what I mean.


The Real World

Now I want to show you a few things that they don’t talk about in the licensing documents that you are likely to run across in your life as an OVM administrator.

  • live migrate a pinned VM from one OVM Server to another

Capture 2

As you can see above, we have 4 VMs running in this cluster.  Below is an overview of prod_db1.  Take note of the ID field, we’ll use it later to identify the VM:


We’re gonna use prod_db1 as our guinea pig for this experiment.  Currently prod_db1 is running on server OVM1 and is pinned to vCPUs 0-3 as noted in the vm.cfg snippet below:


I also have a VM running on server ovm2 that is pinned to the very same vCPUs:


One would think you cannot live migrate the VM from ovm1 to ovm2 because of the fact that prod_db3 is already pinned to the same vCPUs on ovm2?

Screenshot (7)


You certainly can perform the live migration.  Here’s what will happen:

  • The VM will successfully migrate to ovm2
  • prod_db1 will only run on vCPUs 0-3 on ovm2
  • prod_db3 will only run on vCPUs 0-3 on ovm2
  • your performance in both VMs will likely go down the drain
  • you will be out of compliance with Oracle hard partition licensing requirements


I’ve had a LOT of people ask me this question, so here’s your proof:

[root@ovm1 ~]# xm vcpu-list
Name ID VCPU CPU State Time(s) CPU Affinity
0004fb00000600000632b8de1db5a014 3 0 20 -b- 3988.0 any cpu
0004fb00000600000632b8de1db5a014 3 1 21 -b- 133.8 any cpu
0004fb00000600008825773ba1661d01 2 0 0 -b- 3083.6 0-3
0004fb00000600008825773ba1661d01 2 1 3 -b- 308.1 0-3
Domain-0 0 0 0 r-- 63990.1 0
Domain-0 0 1 1 r-- 62421.0 1
Domain-0 0 2 2 -b- 16102.8 2
Domain-0 0 3 3 -b- 10355.7 3
Domain-0 0 4 4 -b- 2718.1 4
Domain-0 0 5 5 -b- 9427.4 5
Domain-0 0 6 6 -b- 5660.8 6
Domain-0 0 7 7 -b- 3932.0 7
Domain-0 0 8 8 -b- 2268.0 8
Domain-0 0 9 9 -b- 8477.9 9
Domain-0 0 10 10 -b- 4950.6 10
Domain-0 0 11 11 -b- 4304.6 11
Domain-0 0 12 12 -b- 2001.5 12
Domain-0 0 13 13 -b- 10321.1 13
Domain-0 0 14 14 -b- 5221.5 14
Domain-0 0 15 15 -b- 3515.0 15
Domain-0 0 16 16 -b- 2408.8 16
Domain-0 0 17 17 -b- 9905.2 17
Domain-0 0 18 18 -b- 6105.3 18
Domain-0 0 19 19 -b- 4504.2 19

[root@ovm2 ~]# xm vcpu-list
Name ID VCPU CPU State Time(s) CPU Affinity
Domain-0 0 0 0 -b- 54065.1 0
Domain-0 0 1 1 -b- 10110.4 1
Domain-0 0 2 2 -b- 4909.4 2
Domain-0 0 3 3 -b- 6344.0 3
Domain-0 0 4 4 -b- 1012.4 4
Domain-0 0 5 5 -b- 6506.3 5
Domain-0 0 6 6 -b- 4163.1 6
Domain-0 0 7 7 -b- 1564.5 7
Domain-0 0 8 8 -b- 1367.5 8
Domain-0 0 9 9 -b- 14307.2 9
Domain-0 0 10 10 -b- 4068.7 10
Domain-0 0 11 11 -b- 1799.4 11
Domain-0 0 12 12 -b- 1731.3 12
Domain-0 0 13 13 -b- 5478.0 13
Domain-0 0 14 14 -b- 6983.5 14
Domain-0 0 15 15 -b- 5781.6 15
Domain-0 0 16 16 -b- 723.4 16
Domain-0 0 17 17 r-- 4922.6 17
Domain-0 0 18 18 r-- 3585.3 18
Domain-0 0 19 19 -b- 1705.8 19
0004fb0000060000c9e5303a8dc2c675 3 0 0 -b- 5556.6 0-3
0004fb0000060000c9e5303a8dc2c675 3 1 3 -b- 144.4 0-3
  • Now I live migrate prod_db1 from ovm1 to ovm2

Screenshot (8)Screenshot (9)

Screenshot (10)


Here is the new vcpu-list post-migration:

[root@ovm1 ~]# xm vcpu-list
Name ID VCPU CPU State Time(s) CPU Affinity
0004fb00000600000632b8de1db5a014 3 0 20 -b- 4007.2 any cpu
0004fb00000600000632b8de1db5a014 3 1 21 -b- 134.4 any cpu
Domain-0 0 0 0 r-- 64376.4 0
Domain-0 0 1 1 r-- 62793.1 1
Domain-0 0 2 2 -b- 16201.5 2
Domain-0 0 3 3 -b- 10418.6 3
Domain-0 0 4 4 -b- 2743.2 4
Domain-0 0 5 5 -b- 9486.1 5
Domain-0 0 6 6 -b- 5702.4 6
Domain-0 0 7 7 -b- 3955.7 7
Domain-0 0 8 8 -b- 2279.8 8
Domain-0 0 9 9 -b- 8530.4 9
Domain-0 0 10 10 -b- 4984.4 10
Domain-0 0 11 11 -b- 4328.3 11
Domain-0 0 12 12 -b- 2013.2 12
Domain-0 0 13 13 -b- 10390.7 13
Domain-0 0 14 14 -b- 5257.2 14
Domain-0 0 15 15 -b- 3542.0 15
Domain-0 0 16 16 -b- 2422.3 16
Domain-0 0 17 17 -b- 9969.5 17
Domain-0 0 18 18 -b- 6150.0 18
Domain-0 0 19 19 -b- 4532.5 19

[root@ovm2 ~]# xm vcpu-list
Name ID VCPU CPU State Time(s) CPU Affinity
0004fb00000600008825773ba1661d01 5 0 2 -b- 1.9 0-3
0004fb00000600008825773ba1661d01 5 1 1 -b- 0.2 0-3
Domain-0 0 0 0 -b- 54418.2 0
Domain-0 0 1 1 -b- 10228.5 1
Domain-0 0 2 2 -b- 4939.8 2
Domain-0 0 3 3 -b- 6373.9 3
Domain-0 0 4 4 -b- 1024.7 4
Domain-0 0 5 5 -b- 6547.6 5
Domain-0 0 6 6 -b- 4218.0 6
Domain-0 0 7 7 -b- 1596.2 7
Domain-0 0 8 8 -b- 1374.9 8
Domain-0 0 9 9 -b- 14341.6 9
Domain-0 0 10 10 -b- 4099.5 10
Domain-0 0 11 11 -b- 1822.6 11
Domain-0 0 12 12 -b- 1737.6 12
Domain-0 0 13 13 r-- 5513.4 13
Domain-0 0 14 14 -b- 7016.8 14
Domain-0 0 15 15 -b- 5814.6 15
Domain-0 0 16 16 -b- 731.6 16
Domain-0 0 17 17 -b- 4960.6 17
Domain-0 0 18 18 -b- 3617.2 18
Domain-0 0 19 19 -b- 1714.2 19
0004fb0000060000c9e5303a8dc2c675 3 0 3 -b- 5590.3 0-3
0004fb0000060000c9e5303a8dc2c675 3 1 0 -b- 145.6 0-3


You can see that both VMs are pinned to the same vCPUs and they’re still running just fine.  Like I said- it will technically work but you’re shooting yourself in the foot in multiple ways if you do this.  Also keep in mind- if you turn on HA for prod_db1 and ovm1 goes down, the VM will fail to start on ovm2 because of the cpu pinning.  Don’t say I didn’t warn you!


  • Apply CPU pinning to a VM online with no reboot

In OVM 3.2 and 3.3, you were able to apply CPU pinning to a VM live without having to restart it.  A bug emerged in OVM 3.4.1 and 3.4.2 that broke this.  However it was fixed in OVM 3.4.3.  So depending on which version of OVM you’re running, you may be able to pin your VMs without having to take a reboot.  Watch and be amazed!


Currently running OVM 3.3.3:

[root@ovm1 ~]# cat /etc/ovs-release
Oracle VM server release 3.3.3


ovm_vmcontrol utilities are installed:

[root@ovmm ovm_util]# pwd
[root@ovmm ovm_util]# ls -la
total 44
drwxrwxr-x 5 root root 4096 Jul 2 2014 .
drwxr-xr-x 11 oracle dba 4096 Aug 29 13:04 ..
drwxrwxr-x 2 root root 4096 Jul 2 2014 class
drwxr-xr-x 2 root root 4096 Jul 2 2014 lib
drwxr-xr-x 3 root root 4096 Jul 2 2014 man
-rwxr-xr-x 1 root root 1229 Jul 2 2014 ovm_reporestore
-rwxr-xr-x 1 root root 1227 Jul 2 2014 ovm_vmcontrol
-rwxr-xr-x 1 root root 1245 Jul 2 2014 ovm_vmdisks
-rwxr-xr-x 1 root root 1245 Jul 2 2014 ovm_vmhostd
-rwxr-xr-x 1 root root 1246 Jul 2 2014 ovm_vmmessage
-rwxr-xr-x 1 root root 2854 Jul 2 2014 vm-dump-metrics


I have an existing VM that is currently allowed to run on any vCPU on the server:

[root@ovm1 ~]# xm vcpu-list
Name ID VCPU CPU State Time(s) CPU Affinity
0004fb00000600000632b8de1db5a014 3 0 20 -b- 4012.8 any cpu
0004fb00000600000632b8de1db5a014 3 1 21 -b- 134.6 any cpu
Domain-0 0 0 0 -b- 64446.0 0
Domain-0 0 1 1 -b- 62820.1 1
Domain-0 0 2 2 -b- 16213.7 2
Domain-0 0 3 3 -b- 10426.0 3
Domain-0 0 4 4 -b- 2746.1 4
Domain-0 0 5 5 -b- 9499.3 5
Domain-0 0 6 6 -b- 5712.5 6
Domain-0 0 7 7 -b- 3960.2 7
Domain-0 0 8 8 -b- 2282.3 8
Domain-0 0 9 9 -b- 8541.0 9
Domain-0 0 10 10 -b- 4992.0 10
Domain-0 0 11 11 -b- 4334.6 11
Domain-0 0 12 12 -b- 2015.6 12
Domain-0 0 13 13 -b- 10404.4 13
Domain-0 0 14 14 -b- 5265.1 14
Domain-0 0 15 15 -b- 3546.7 15
Domain-0 0 16 16 -b- 2423.7 16
Domain-0 0 17 17 r-- 9983.8 17
Domain-0 0 18 18 -b- 6158.2 18
Domain-0 0 19 19 -b- 4536.8 19


Now let’s pin that VM to vcpu 8-11:

[root@ovmm ovm_util]# ./ovm_vmcontrol -u admin -p ******** -h localhost -v prod_db2 -c vcpuset -s 8-11
Oracle VM VM Control utility 2.0.1.
Connecting with a secure connection.
Command : vcpuset
Pinning virtual CPUs
Pinning of virtual CPUs to physical threads '8-11' 'prod_db2' completed.


And here’s our proof that the pinning is applied immediately with no reboot:

[root@ovm1 ~]# xm vcpu-list
Name ID VCPU CPU State Time(s) CPU Affinity
0004fb00000600000632b8de1db5a014 3 0 10 -b- 4013.6 8-11
0004fb00000600000632b8de1db5a014 3 1 8 -b- 134.6 8-11
Domain-0 0 0 0 -b- 64454.8 0
Domain-0 0 1 1 -b- 62823.2 1
Domain-0 0 2 2 -b- 16215.2 2
Domain-0 0 3 3 -b- 10427.0 3
Domain-0 0 4 4 -b- 2746.3 4
Domain-0 0 5 5 r-- 9500.6 5
Domain-0 0 6 6 -b- 5713.6 6
Domain-0 0 7 7 -b- 3960.6 7
Domain-0 0 8 8 -b- 2282.5 8
Domain-0 0 9 9 -b- 8542.9 9
Domain-0 0 10 10 -b- 4992.8 10
Domain-0 0 11 11 -b- 4335.0 11
Domain-0 0 12 12 -b- 2015.8 12
Domain-0 0 13 13 -b- 10406.7 13
Domain-0 0 14 14 -b- 5266.4 14
Domain-0 0 15 15 -b- 3547.2 15
Domain-0 0 16 16 -b- 2424.2 16
Domain-0 0 17 17 -b- 9984.8 17
Domain-0 0 18 18 -b- 6159.6 18
Domain-0 0 19 19 -b- 4537.6 19


You’ll just have to take my word that I didn’t reboot the VM inbetween the steps- which should be validated by the time column for that VM (note that it increased a little, not reset to 0).



Well- happy hunting for now!


OVM CPU Pinning



Oracle has published a few documents (2240035.1 and 2213691.1 for starters) about CPU pinning in relation to hard partitions for VMs running on OVM.  This is to avoid having to license every core on the server (like you have to with VMware) for Oracle products that are licensed per core or per user.


I’m going to provide an excel spreadsheet at the end of this post that will help you visualize which VM is pinned to which CPU and if there is any overlap.  When a VM is not pinned to a given CPU, it is allowed to run on any cpu within the constraints of the Xen scheduler and where it wants the VM to run.  It will take into account things like NUMA and core boundaries to avoid scheduling a VM in a way that is inefficient.


You will need to modify this spreadsheet to fit your server configuration.  Use the information in the ovm-hardpart-168217 document to figure out what your systems CPU topology looks like.


A couple things to keep in mind:

  • You cannot live migrate a VM that is pinned.  Technically it will work and the VM will migrate. but Oracle does not allow this based on the terms of their hard partitioning license.  See attached document ovm-hardpart-168217 at the end of this post for more information.
  • When you pin a VM to a vCPU or range of vCPUs, that VM can only run on those vCPUs.  However, if you have other VMs that are not pinned, they can run on any vCPU on the system- including the ones that you just pinned your production database to!  If you have a combination of pinned and unpinned VMs, pin all the other VMs to the range of vCPUs that you want to lock them to.  This way, they can’t run on any vCPUs that you’ve already pinned VMs to.
  • Remember that DOM0 has to be scheduled to run just like the other resources.  Based on how big your system is, OVM will run DOM0 on the first few vCPUs.  This shouldn’t be a problem unless your DOM0 is extremely busy doing work such as processing I/O for the VMs that are running and handling interrupts.  In this case, if you have VMs that are pinned to the same vCPUs as DOM0 you might have some performance problems.  I’ve outlined where DOM0 runs by default on the size system in the example.
  • Realize that you can pin more than one VM to a vCPU.  I wouldn’t recommend this for obvious performance reasons but it’s possible to do.  This is where the spreadsheet comes in handy.
  • If you’re installing the ovm utilities which provides ovm_vmcontrol, you may need to enable remote connections first.  If you get an error message stating that there is an error connecting to localhost, perform the steps below.  You have to pay attention to the version of the ovm utilites that you install.  The readme will show you which of the three (currently) versions to install based on the version of OVM you’re running.
  • Below are the steps to enable remote connections (this was taken from Douglas Hawthorne’s blog here).  Note that the steps below should be performed as the root user, not oracle:
[root@melbourne ~]# cd /u01/app/oracle/ovm-manager-3/bin
[root@melbourne bin]# ./
Generate OVMM TCP over SSH key store by following steps:
Enter keystore password:
Re-enter new password:
What is your first and last name?
 [Unknown]: OVM
What is the name of your organizational unit?
 [Unknown]: melbourne
What is the name of your organization?
 [Unknown]: YAOCM
What is the name of your City or Locality?
 [Unknown]: Melbourne
What is the name of your State or Province?
 [Unknown]: Victoria
What is the two-letter country code for this unit?
 [Unknown]: AU
Is CN=OVM, OU=melbourne, O=YAOCM, L=Melbourne, ST=Victoria, C=AU correct?
 [no]: yes

Enter key password for <ovmm>
 (RETURN if same as keystore password):
Re-enter new password:
[root@melbourne bin]# ./
Enabling OVMM TCP over SSH service

Please enter the Oracle VM manager user name: admin

Please enter the Oracle VM manager user password:

Please enter the password for TCPS key store :

The job of enabling OVMM TCPS service is committed, please restart OVMM to take effect.

[root@melbourne ~]# service ovmm restart
Stopping Oracle VM Manager [ OK ]
Starting Oracle VM Manager [ OK ]


If you have any questions- feel free to post them here.  Good luck!



CPU pinning example


OVM Manager Cipher Mismatch fix

I was installing a virtual OVM 3.3.3 test environment the other day and when I got to logging into OVM Manager for the first time I got this error:


This has to due with the fact that most modern browsers have dropped support for the older RC4 encryption cipher which is what OVM Manager uses.  There is a “fix” until you update to a newer version that has this bug patched.  See InfoDoc 2099148.1 for all the details, but here’s the meat of it:


  • Make a backup of the Weblogic config file
# cd /u01/app/oracle/ovm-manager-3/domains/ovm_domain/config
# cp config.xml config.xml.bak


  • Add the following line to the cihpersuite section (search for ciphersuite)


  • Restart the ovm manager service and all is well
# service ovmm restart

Configure simple DNS server on RHEL 6

Sometimes when setting up hardware for a customer, it makes things a lot easier if I can simulate their network in our lab.  This allows me to deploy the solution plug and play without having to re-ip a bunch of stuff or wait until I’m on their network to do most of the install.  A couple problems I’ve come across are access to the internet for patches/updates and DNS.


I’ve generally used an old netgear or linksys router to front the customer’s internal network inside my lab environment and just connect it to the back of our cable modem.  This solves the first problem- internet access.  The other problem is a bit more involved, since you have to have a DNS server on that network (preferrably on the same IP address as in the real network when it’s deployed) I’ve taken to using Linux as a stepping stone.  It’s really simple to install Linux or grab one that’s already there and plug it into my private sandbox.  Once that’s done, you just need to install and configure a DNS server.  Here is the step by step process (your IP network will be different, just substitute where appropriate). FYI- I’m running Oracle Linux 6.7 with the Red Hat Compatible Kernel for this tutorial. CentOS 6.7 and RHEL 6.7 are no different other than the repositories you point to in order to get your patches.

Let’s install BIND (Berkley Internet Name Domain) better known as DNS

# yum install -y bind bind-utils
[root@tempDNS ~]# yum install -y bind bind-utils
Loaded plugins: refresh-packagekit, security, ulninfo
Setting up Install Process
public_ol6_latest                                                                                            | 1.4 kB     00:00
Resolving Dependencies
--> Running transaction check
---> Package bind.x86_64 32:9.8.2-0.62.rc1.el6_9.2 will be installed
--> Processing Dependency: bind-libs = 32:9.8.2-0.62.rc1.el6_9.2 for package: 32:bind-9.8.2-0.62.rc1.el6_9.2.x86_64
---> Package bind-utils.x86_64 32:9.8.2-0.37.rc1.el6 will be updated
---> Package bind-utils.x86_64 32:9.8.2-0.62.rc1.el6_9.2 will be an update
--> Running transaction check
---> Package bind-libs.x86_64 32:9.8.2-0.37.rc1.el6 will be updated
---> Package bind-libs.x86_64 32:9.8.2-0.62.rc1.el6_9.2 will be an update
--> Finished Dependency Resolution

Dependencies Resolved

 Package                   Arch                  Version                                     Repository                        Size
 bind                      x86_64                32:9.8.2-0.62.rc1.el6_9.2                   public_ol6_latest                4.0 M
 bind-utils                x86_64                32:9.8.2-0.62.rc1.el6_9.2                   public_ol6_latest                188 k
Updating for dependencies:
 bind-libs                 x86_64                32:9.8.2-0.62.rc1.el6_9.2                   public_ol6_latest                891 k

Transaction Summary
Install       1 Package(s)
Upgrade       2 Package(s)

Total download size: 5.1 M
Downloading Packages:
(1/3): bind-9.8.2-0.62.rc1.el6_9.2.x86_64.rpm                                                                | 4.0 MB     00:00
(2/3): bind-libs-9.8.2-0.62.rc1.el6_9.2.x86_64.rpm                                                           | 891 kB     00:00
(3/3): bind-utils-9.8.2-0.62.rc1.el6_9.2.x86_64.rpm                                                          | 188 kB     00:00
Total                                                                                               3.8 MB/s | 5.1 MB     00:01
warning: rpmts_HdrFromFdno: Header V3 RSA/SHA256 Signature, key ID ec551f03: NOKEY
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-oracle
Importing GPG key 0xEC551F03:
 Userid : Oracle OSS group (Open Source Software group) 
 Package: 6:oraclelinux-release-6Server-7.0.5.x86_64 (@anaconda-OracleLinuxServer-201507280245.x86_64/6.7)
 From   : /etc/pki/rpm-gpg/RPM-GPG-KEY-oracle
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Updating   : 32:bind-libs-9.8.2-0.62.rc1.el6_9.2.x86_64                                                                       1/5
  Updating   : 32:bind-utils-9.8.2-0.62.rc1.el6_9.2.x86_64                                                                      2/5
  Installing : 32:bind-9.8.2-0.62.rc1.el6_9.2.x86_64                                                                            3/5
  Cleanup    : 32:bind-utils-9.8.2-0.37.rc1.el6.x86_64                                                                          4/5
  Cleanup    : 32:bind-libs-9.8.2-0.37.rc1.el6.x86_64                                                                           5/5
  Verifying  : 32:bind-utils-9.8.2-0.62.rc1.el6_9.2.x86_64                                                                      1/5
  Verifying  : 32:bind-9.8.2-0.62.rc1.el6_9.2.x86_64                                                                            2/5
  Verifying  : 32:bind-libs-9.8.2-0.62.rc1.el6_9.2.x86_64                                                                       3/5
  Verifying  : 32:bind-libs-9.8.2-0.37.rc1.el6.x86_64                                                                           4/5
  Verifying  : 32:bind-utils-9.8.2-0.37.rc1.el6.x86_64                                                                          5/5

  bind.x86_64 32:9.8.2-0.62.rc1.el6_9.2

  bind-utils.x86_64 32:9.8.2-0.62.rc1.el6_9.2

Dependency Updated:
  bind-libs.x86_64 32:9.8.2-0.62.rc1.el6_9.2


Ok, now that we have that done, let’s do a system update to make sure we have all the latest bits and bytes. If this is a production system, consult your companys policy on updates and patches before doing this. I don’t want to be responsible for making the other applications on this server potentially not work for any reason.

[root@tempDNS ~]# yum update -y
Loaded plugins: refresh-packagekit, security, ulninfo
Setting up Update Process
Resolving Dependencies
--> Running transaction check
---> Package ConsoleKit.x86_64 0:0.4.1-3.el6 will be updated
---> Package ConsoleKit.x86_64 0:0.4.1-6.el6 will be an update
---> Package ConsoleKit-libs.x86_64 0:0.4.1-3.el6 will be updated

At this point, I generally recommend a reboot so any updates that had prerequisites for a reboot are taken care of. Also it just makes sure the system is at a known good place for our work.

Let’s edit the /etc/named.conf file and replace the options section with our own custom code:

options {
    listen-on port 53 {;; };
        #listen-on-v6 port 53 { ::1; };
        directory   "/var/named";
        dump-file   "/var/named/data/cache_dump.db";
        statistics-file "/var/named/data/named_stats.txt";
        memstatistics-file "/var/named/data/named_mem_stats.txt";
        allow-query { any; };
        allow-transfer     { localhost; };
        recursion yes;

        dnssec-enable yes;
        dnssec-validation yes;
        dnssec-lookaside auto;

        /* Path to ISC DLV key */
        bindkeys-file "/etc/named.iscdlv.key";

        managed-keys-directory "/var/named/dynamic";

Note above that I have added my local IP address to the end of the listen-on line. Now let’s add a couple zone files.

zone "" IN {
                type master;
                file "";
                allow-update { none; };

zone "" IN {
                type master;
                file "";
                allow-update { none; };

Obviously change the domain name to your own on the zone line and the file line. Leave the .zone at the end though.

Here are the two files you want to put into /var/named/

$TTL 86400
@   IN  SOA (
        2017062601  ;Serial
        3600        ;Refresh
        1800        ;Retry
        604800      ;Expire
        86400       ;Minimum TTL
; Specify our nameserver
                IN      NS    

; Resolve nameserver hostname to IP
ns1             IN      A     

; Define hostname -> IP pairs which you wish to resolve
gateway         IN      A     

$TTL 86400
@       IN      SOA (
                        21600      ; refresh after 6 hours
                        3600       ; retry after 1 hour
                        604800     ; expire after 1 week
                        86400 )    ; minimum TTL of 1 day
@       IN      NS
1       IN      PTR

There are a few things I’d like you to note.

1) You have to update the serial number any time you make a change to the zone file (forward or reverse). I usually use the format YYYYMMDD## where ## is a sequential number starting with 01. This way if you make multiple updates on the same day, the root servers on the internet will know which version is current.

2) Take notice of the . at the end of the entries in the reverse zone file. These have to be there- they terminate the domain hierarchy and tell the server that this is the root so it doesn’t try to keep looking any further.

3) In my example above, I also have an entry for which has an IP address of This is not normally something you would need or want to do but I wanted to show the syntax of how to do it.

4) For every record you want to add to DNS, it’s a good idea to make sure you also add a reverse record. This lets you do an nslookup or dig against the IP address and it will return the name. A lot of stuff will break or at the very least give you problems if it’s not in place so just get in the habit of doing it.

That’s pretty much it. There are a lot of other nuances that I don’t need to get into here. I almost didn’t write this because there are so many tutorials out there that IMHO are written better than mine. Mainly I wanted to keep it for my own use so I know right where to go when I need to install a quick and dirty DNS server. Hopefully one of you will benefit from this.


Virtualized ODA X6-2HA – working with VMs

It’s been awhile since I built a virtualized ODA with VMs on a shared repo so I thought I’d go through the basic steps.

  1. install the OS
    1. install Virtual ISO image
    2. configure networking
    3. install ODA_BASE patch
    4. deploy ODA_BASE
    5. configure networking in ODA_BASE
    6. deploy ODA_BASE with configurator
  2. create shared repository.  This is where your specific situation plays out.  Depending on your hardware you may have less or more space in DATA or RECO.  Your DBA will be able to tell you how much they need for each and where you can borrow a few terabytes (or however much you need) for your VMs
  3. (optionally) create a separate shared repository to store your templates.  This all depends on how many of the same kind of VM you’ll be deploying.  If it makes no sense to keep the templates around once you create your VMs then don’t bother with this step
  4. import template into repository
    1. download the assembly file from Oracle (it will unzip into an .ova archive file)
    2. ***CRITICAL*** copy the .ova to /OVS on either nodes’ DOM0, not into ODA_BASE
    3. import the assembly (point it to the file sitting in DOM0 /OVS)
  5. modify template config as needed (# of vCPUs, Memory, etc)
  6. clone the template to a VM
  7. add network to VM (usually net1 for first public network, net2 for second and net3+ for any VLANs you’ve created
  8. boot VM and start console (easiest way is to VNC into ODA_BASE and launch it from there)
  9. set up your hostname, networking, etc the way you want it
  10. reboot VM to ensure changes persist
  11. rinse and repeat as needed

If you need to configure HA, preferred node or any other things, this is the time to do it.


Create VM in Oracle VM for x86 using NFS share

I’m using OVM Manager 3.4.2 and OVM Server 3.3.2 to test an upgrade for one of our customers.  I am using Starwind iSCSI server to present the shared storage to the cluster but in production you should use enterprise grade hardware to do this.  There’s an easier way to do this- create an HVM VM and install from an ISO stored in a repository.  Then power the VM off and change the type to PVM then power on.  This may not work with all operating systems however so I’m going over how to create a new PVM VM from an ISO image shared from an NFS server.

* Download ISO (I'm using Oracle Linux 6.5 64bit for this example)
* Copy ISO image to OVM Manager (any NFS server is fine)
* Mount ISO on the loopback device
# mount -o loop /var/tmp/V41362-01.iso /mnt

* Share the folder via NFS
# service nfs start
Starting NFS services: [ OK ]
Starting NFS quotas: [ OK ]
Starting NFS mountd: [ OK ]
Starting NFS daemon: [ OK ]
Starting RPC idmapd: [ OK ]

# exportfs *:/mnt/

# showmount -e
Export list for ovmm:
/mnt *

* Create new VM in OVM Manager
* Edit VM properties and configure as PVM
* Set additional properties such as memory, cpu and network
* At the boot order tab, enter the network boot path formatted like this:
  nfs:{ip address or FQDN of NFS host}:/{path to ISO image top level directory}

For example, our NFS server is and the path where I mounted the ISO is at /mnt.  Leave the {}'s off of course:


You should be able to boot your VM at this point and perform the install of the OS.

Nimble PowerShell Toolkit

I was working on an internal project to test performance of a converged system solution.  The storage component is a Nimble AF7000 from which we’re presenting a number of LUNs.  There are almost 30 LUNs and I’ve had to create, delete and provision them a number of times throughout the project.  It became extremely tedious to do this through the WebUI so I decided to see if it could be scripted.

I know you can log into the nimble via ssh and basically do what I’m trying to do- and I did test this with success.  However I’ve recently had a customer who wanted to use PowerShell to perform some daily snapshot/clone operations for Oracle database running on windows (don’t ask).  We decided to leverage the Nimble PowerShell Toolkit to perform the operations right from the windows server.  The script was fairly straightforward, although we had to learn a little about PowerShell syntax and such.  I’ve included a sanitized script below that basically does what I need to.

$arrayname = "IP address or FQDN of array management address"
$nm_uid = "admin"
$nm_password = ConvertTo-SecureString -String "admin" -AsPlainText -Force
$nm_cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $nm_uid,$nm_password
$initiatorID = Get-NSInitiatorGroup -name {name of initiator group} | select -expandproperty id

# Import Nimble Tool Kit for PowerShell
import-module NimblePowerShellToolKit

# Connect to the array
Connect-NSGroup -group $arrayname -credential $nm_cred

# Create 10 DATA Disks
for ($i=1; $i -le 10; $i++) {
    New-NSVolume -Name DATADISK$i -Size 1048576 -PerfPolicy_id 036462b75de9a4f69600000000000000000000000e -online $true
    $volumeID = Get-NSVolume -name DATADISK$i | select -expandproperty id
    New-NSAccessControlRecord -initiator_group_id $initiatorID -vol_id $volumeID

# Create 10 RECO Disks
for ($i=1; $i -le 10; $i++) {
    New-NSVolume -Name RECODISK$i -Size 1048576 -PerfPolicy_id 036462b75de9a4f69600000000000000000000000e -online $true
    $volumeID = Get-NSVolume -name RECODISK$i | select -expandproperty id
    New-NSAccessControlRecord -initiator_group_id $initiatorID -vol_id $volumeID

# Create 3 GRID Disks
for ($i=1; $i -le 3; $i++) {
    New-NSVolume -Name GRIDDISK$i -Size 2048 -PerfPolicy_id 036462b75de9a4f69600000000000000000000000e -online $true
    $volumeID = Get-NSVolume -name GRIDDISK$i | select -expandproperty id
    New-NSAccessControlRecord -initiator_group_id $initiatorID -vol_id $volumeID

I also wrote a script to delete the LUNs below:

$arrayname = "IP address or FQDN of array management address"  
$nm_uid = "admin"
$nm_password = ConvertTo-SecureString -String "admin" -AsPlainText -Force
$nm_cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $nm_uid,$nm_password
$initiatorID = Get-NSInitiatorGroup -name {name of initiator group} | select -expandproperty id

# Import Nimble Tool Kit for PowerShell
import-module NimblePowerShellToolKit

# Connect to the array 
Connect-NSGroup -group $arrayname -credential $nm_cred

# Delete 10 DATA Disks
for ($i=1; $i -le 10; $i++) {
    Set-NSVolume -name DATADISK$i -online $false
    Remove-NSVolume -name DATADISK$i

# Delete 10 RECO Disks
for ($i=1; $i -le 10; $i++) {
    Set-NSVolume -name RECODISK$i -online $false
    Remove-NSVolume -name RECODISK$i 

# Delete 3 GRID Disks
for ($i=1; $i -le 3; $i++) {
    Set-NSVolume -name GRIDDISK$i -online $false
    Remove-NSVolume -name GRIDDISK$i 

Obviously you’ll have to substitute some of the values such as $arrayname, $nm_uid, $nm_password and $initiatorID (make sure you remove the {}’s when you put your value here). This is a very insecure method of storing your password but it was a quick and dirty solution at the time. There are ways to store the value of a password from a highly secured text file and encrypt it into a variable. Or if you don’t mind being interactive, you can skip providing the credentials and it will pop up a password dialog box for you to enter them every time the script runs.

It made the project go a lot faster- hopefully you can use this to model different scripts to do other things. The entire command set of the Nimble array is basically exposed through the toolkit so there’s not a whole lot you can’t do here that you could in the WebUI. When you download the toolkit- there is a README PDF that goes through all the commands. When in PowerShell, you can also get help for each of the commands. For example:

PS C:\Users\esteed> help New-NSVolume


    Create operation is used to create or clone a volume. Creating volumes requires name and size attributes. Cloning
    volumes requires clone, name and base_snap_id attributes where clone is set to true. Newly created volume will not
    have any access control records, they can be added to the volume by create operation on access_control_records
    object set. Cloned volume inherits access control records from the parent volume.

    New-NSvolume [-name] <String> [-size] <UInt64> [[-description] <String>] [[-perfpolicy_id] <String>] [[-reserve]
    <UInt64>] [[-warn_level] <UInt64>] [[-limit] <UInt64>] [[-snap_reserve] <UInt64>] [[-snap_warn_level] <UInt64>]
    [[-snap_limit] <UInt64>] [[-online] <Boolean>] [[-multi_initiator] <Boolean>] [[-pool_id] <String>] [[-read_only]
    <Boolean>] [[-block_size] <UInt64>] [[-clone] <Boolean>] [[-base_snap_id] <String>] [[-agent_type] <String>]
    [[-dest_pool_id] <String>] [[-cache_pinned] <Boolean>] [[-encryption_cipher] <String>] [<CommonParameters>]

    Create operation is used to create or clone a volume. Creating volumes requires name and size attributes. Cloning
    volumes requires clone, name and base_snap_id attributes where clone is set to true. Newly created volume will not
    have any access control records, they can be added to the volume by create operation on access_control_records
    object set. Cloned volume inherits access control records from the parent volume.


    To see the examples, type: "get-help New-NSvolume -examples".
    For more information, type: "get-help New-NSvolume -detailed".
    For technical information, type: "get-help New-NSvolume -full".

You can also use the -detail parameter at the end to get a more complete description of each option. Additionally you can use -examples to see the commands used in real world situations. Have fun!

Temperature monitoring script with email alerts

We have quite a bit of expensive equipment in our server room and we’ve had the A/C fail a couple times. As a result, I’ve installed a raspberry pi zero with a DS18B20 temperature sensor connected to it to monitor the temperature of the room.  If it goes above a set threshold, it will send an email to the engineers so we can log in and shut stuff down until the problem is fixed.


This project branches off from the one I did earlier on monitoring temperature with a raspberry pi and MRTG.  This too uses MRTG but I won’t get into the details of that- you can see how I set that up here.


The big piece here is the alerting logic.  You’d be surprised how fast the temp can go up in a small room with lots of gear putting out a lot of heat.  For that reason, I monitor the temperature every minute.  If the current temperature exceeds the threshold I set in the script, it fires an email and sets an alert flag to true.  The reason I did this is so we don’t get an email every minute while the temperature is above threshold.  How irritating would that be?  So another piece of logic in the script checks to see if the alert flag has been tripped.  If it has, no email is sent until the temperature comes down below the threshold.  Then an all clear email is sent and the cycle repeats itself.


I used the instructions here to set up ssmtp on the pi.  In my case, I used our comcast email relay since we have comcast so the instructions for that are a little different.  You can also use your company’s own mail relay if you have one internally that can be used to send email to external addresses.  As has been my practice lately, I’ve uploaded the code to GitHub here for you to do with as you please.


As always, if you have any constructive criticism or comments, feel free to leave them below and I’ll get back to you ASAP.

SSH Tunneling with PuTTY

From time to time I have a need to connect to a system inside another remote network (usually my work).  Normally I just ssh in and then jump to the machine I need to be on.  That’s all fine and dandy if you don’t need a GUI.  What if you need to be on the GUI console of the target machine inside the firewall and the firewall doesn’t allow the port you need to use?


Enter VNC and PuTTY.  You aren’t limited to doing this with PuTTY or VNC.  It’s just that a majority of my work is done from a windows machine and I refuse to install the bloated CYGWIN app on my machine just to get an ssh command line session.  Bah.. that’s a story for another day.  Anyway- SSH tunnels can be a bit confusing to the lay person so I thought I’d do a graphical illustration to help that out.


In this scenario, I will be using my laptop at home to connect into a landing pad UNIX machine at work.  I will then open a tunnel to another machine inside the remote network that will establish a connection to the VNC server running on that machine.  I won’t go into how to set up a VNC Server on linux as there are plenty of tutorials out there that will cover it.  The one thing I will say is make sure you use a password when you start it up.  This is a visual example of what the connection looks like:




Here are some enlarged views so you can see what’s going on.  First we start PuTTY on the laptop.  I’ll show an example of what options you need to select inside the Putty connection later.  Once the tunnel is in place, fire up your favorite VNC client and point it to or localhost on port 59001:


We pointed our VNC client to the address and port of the tunnel we just created, so the traffic is sent through the tunnel into the external Landing Pad and being forwarded on into the remote network:


Finally, the tunnel terminates on the server inside the remote network and connects the tunnel to port 5901 on that machine:



It may seem odd to connect your VNC client to the laptop’s localhost address in order to reach the target machine.  This is because you’re sending that traffic through the SSH tunnel that we set up rather than pointing it directly to the server you want to reach.


Now I’ll show you how to configure PuTTY to create the tunnel.  First, fire up Putty and populate the username and IP address of the landing pad server in our example (substitute yours of course).  Leave the port at 22:



Next, scroll down on the left hand side in the Category window and select Tunnels.  Here, populate the source port (59001 in my example), the IP address of the final destination server along with the port you want to connect to on that machine (5901 in my example).  Remember, you aren’t putting the IP address of the landing pad here- we want the target server in the Destination field. Once you have the Source port and Destination fields filled in, click Add and it will pop into the window as seen below:



To establish the tunnel, click Open. This will launch the PuTTY terminal and prompt you for your password.  In this screenshot, I’m using root to log in however generally it’s a good idea to use a non-privileged user to log into any machine:



Once you see the user prompt and you’re logged in, the tunnel is now in place.  Keep in mind that this SSH session you have open is the only thing keeping that tunnel open.  If you log out of the shell, it also tears down the tunnel so keep this window open while you’re using the tunnel.


The next step is to launch a VNC Viewer on your laptop and point it to your local machine on port 59001:


Click the connect button and you should see the next window prompting you for the password you set up earlier:


Finally, once you click OK you will be brought to your VNC Desktop on the machine inside the remote network!



So let’s take a step back and review what we’ve effectively done here:


Start VNC server:

We have to start a VNC server on the target computer, along with configuring a password to keep everyone else out.  This would have to be done separately.


Establish Tunnel:

We first establish the tunnel from the laptop, through the landing pad and finally to the remote server.  I’m making the obvious assumption here that you have the landing pad accessible to the internet on port 22 and that you have an SSH server running that will accept such connections.  You’re effectively logging into the landing pad just like you would on any other day.  The difference here is that we’re also telling PuTTY to set up a tunnel for us pointing to the remote server as well.  Aside from that- your login session will look and feel just the same.


Launch VNC Client:

We then start the VNC client on our laptop.  Normally, we would point it directly to the server we want to VNC into.  In our case, we created a tunnel that terminates on your laptop at port 59001.  So we connect our VNC client to the laptop (localhost or should work) and point it to port 59001 instead of the standard port 5901.  The VNC client doesn’t care how the traffic is getting to the VNC server, it just does its job.

Think of this SSH tunnel as kind of a wormhole if that type of thing were to actually exist.  The traditional method of connecting to your remote endpoint would be similar to pointing our space shuttle towards the Andromeda galaxy which is about 2.5 million light years away.  It’s essentially not possible to get there- similar to a firewall that is blocking us.  But what if there were a wormhole that terminated near Earth that ended in the Andromeda galaxy?  If we were to point our space shuttle into the wormhole, theoretically we would pop out the other side at our target.


If you do plan on doing something like this, make sure you network administrator is ok with it.  They may detect the traffic as malicious if they’re not sure where it’s coming from and you may wind up in trouble.  I hope this helps give a basic understanding of how SSH Tunnels work.






Internet Ping Meter (part 2 of 2)

Onto the fun stuff!  Below is the python script that does most of the heavy lifting.  Remember with Python, indentation is critical.  It’s actually used to delimit things like functions rather than more traditional delimiters like {}.  Best practice is to use spaces, not tabs for indentation because they can be inconsistent and cause problems.  To avoid this, I like to use an IDE such as notepad++ or the Arduino IDE. It does a great job of taking care of the spacing and indentation. It will even go through the entire script and fix any indentation errors you have automatically. Highly recommended. FYI- You’ll also need to install the PySerial module for this to work:


## Internet Ping Meter v1.0 - Eric Steed
## 01/03/17 - first version - EPS
import serial
import sys
import subprocess
import time
latency = 0
retVal = 0
failLevel = 0
lastLEDStatus = ""

## Define array variable alertLevel[] and assign color codes to be sent to the NeoPixel.
## Based on the number of total ping failures, iterate the failLevel by one and
## send the appropriate color code.
clearLED = "ic"
alertLevel = []
alertLevel = ["h","g","f","e","d"]

## Open the serial port to talk to the NeoPixel. Have to wait for it to initialize
## before we start sending signals
port = serial.Serial("/dev/ttyACM0", baudrate=9600, timeout=1)

## Green = h
## Greenish Yellow = g
## Yellow = f
## Orange = e
## Red = d
## Black = i
## LED #'s
## 1-9 = 1-9
## 10 = a
## 11 = b
## 12 = c
## I'm using a NeoPixel ring with 12 LED Segments to indicate the average latency of
## multiple established servers on the internet.  This way I can tell visually if
## my internet connection is slow, or even down.
## To control the NeoPixel, I've assigned specific characters to indicate how many
## LED's to illuminate and what color.  When we tell the NeoPixel to illuminate a
## given number of LED's, we have to account for the fact that the last command
## string that was sent is persistent in that the LED stays lit even when the next
## command string comes in.  For example, if reading 1 determines that 4 LED's
## should be lit, then reading 2 calls for 3 LED's, you wouldn't be able to see that
## because all 4 LED's were still illuminated from the previous cycle.
## To account for this, we send an instruction to "illuminate" all 12 LED's with
## the color Black before sending the actual value desired.  This is done by
## assigning a value of 'ic' to the variable clearLED.  I've also added some logic
## at the end of the infinite while loop that says don't send any instructions
## unless there's been a change since the last one.  This gets rid of the blinking
## effect that I was seeing on every update- rather annoying!

## I'm using the subprocess library for now unless I can get the native Python ping library
## to do it for me.  If stdout is null for a given target, return 0.
def doPing(host):
    import os,platform
    pingOutput = subprocess.Popen(["ping -c 1 -w 1 " + host + " | grep rtt | awk -F/ '{print $5}' | awk -F. '{print $1}'"], stdout=subprocess.PIPE, shell=True)
    (out, err) = pingOutput.communicate()
    if (out.rstrip('\n') == ''):
        return 0
        return out.rstrip('\n')

## Get average latency from all of the ping targets. Had to cast the output of
## doPing() into an integer to be able to do math against it
while True:
    for x in ping_targets.split():
        retVal = int(doPing(x))
        #print "latency = [{0}]".format(retVal)
        # print "type = [{0}]".format(type(retVal))
        if (retVal > 0):
            latency += retVal

    ## If count is zero, that means we were not able to successfully ping
    ## any of the targets and we should start incrementing the failure count.
    ## Furthermore, if we have been incrementing failLevel and we are now
    ## able to ping, reset the failLevel back to 0 at that time.
    if (count == 0):
        # Increase failure level
        #print "Failed to ping any host"
        failLevel += 1
        if (failLevel > 4):
            failLevel = 4

        failLevel = 0

    ## Set LEDStatus to the appropriate value based on latency and failure count

    #print "Average Latency = [{0}]".format(latency)

            if (latency > 1) and (latency <= 10):                 #print "1-10"                 LEDStatus = clearLED + alertLevel[failLevel] + "1"         elif (latency >= 11) and (latency <= 20):                 #print "11-20"                 LEDStatus = clearLED + alertLevel[failLevel] + "2"         elif (latency >= 21) and (latency <= 30):                 #print "21-30"                 LEDStatus = clearLED + alertLevel[failLevel] + "3"         elif (latency >= 31) and (latency <= 40):                 #print "31-40"                 LEDStatus = clearLED + alertLevel[failLevel] + "4"         elif (latency >= 41) and (latency <= 50):                 #print "41-50"                 LEDStatus = clearLED + alertLevel[failLevel] + "5"         elif (latency >= 51) and (latency <= 60):                 #print "51-60"                 LEDStatus = clearLED + alertLevel[failLevel] + "6"         elif (latency >= 61) and (latency <= 70):                 #print "61-70"                 LEDStatus = clearLED + alertLevel[failLevel] + "7"         elif (latency >= 71) and (latency <= 80):                 #print "71-80"                 LEDStatus = clearLED + alertLevel[failLevel] + "8"         elif (latency >= 81) and (latency <= 90):                 #print "81-90"                 LEDStatus = clearLED + alertLevel[failLevel] + "9"         elif (latency >= 91) and (latency <= 100):
                #print "91-100"
                LEDStatus = clearLED + alertLevel[failLevel] + "a"

                #print "latency greater than 101"
                LEDStatus = clearLED + alertLevel[failLevel] + "c"

    ## If the latency is within a different range than the last iteration, send
    ## the command to update the LED count on the NeoPixel.  Otherwise you get
    ## a rather annoying blinking effect as the LED's are updated even if it's the
    ## same measurement as the last time.
    if (LEDStatus != lastLEDStatus):
        lastLEDStatus = LEDStatus

    #print LEDStatus
    latency = 0

I left the debugging code in the script if you want to uncomment them and watch the terminal as the script runs to see what’s going on. Most of the script is fairly straightforward so I won’t dwell too much on explaining it step by step.

Now, onto the Arduino code. I’m using the Arduino basically as a driver for the NeoPixel. Again- I could have probably just used the Pi by itself, but what fun would that be?

#include <Adafruit_NeoPixel.h>

// Internet Ping Meter v1.0 - Eric Steed
// 01/03/17 - first version - EPS
// Set up variables
byte leds = 0;
uint8_t delayVal = 30;

// Set the PIN number that the NeoPixel is connected to
#define PIN   7

// How bright are the LED's (0-255)
#define INTENSITY 60

// Set color to Green to start
uint8_t  r = 0;
uint8_t  g = INTENSITY;
uint8_t  b = 0;

// Set the number of pixels on the NeoPixel
#define NUMPIXELS   12

// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// Initialize everything and prepare to start
void setup()
  uint8_t i;

  // Set up the serial port for communication
  Serial.println("Started Serial Monitor");

  // This initializes the NeoPixel library.

  // This sets all the pixels to "off"
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 0, 0));;

  // Cycle each pixel through the primary colors to make sure they work, then turn them all off
  // Red
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(INTENSITY, 0, 0));;

  // Green
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, INTENSITY, 0));;

  // Blue
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 0, INTENSITY));;

  // White
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(INTENSITY, INTENSITY, INTENSITY));;

  // Turn off all LED's
  for (i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(0, 0, 0));;

// Main loop
// When sending LED signals, send the color code first, then the number of LED's to
// turn on.  For example 6 Green LED's would be h6, 11 Red LED's would be db, all
// 12 LED's to Black would be ic
void loop()
  uint8_t i;
  if (Serial.available())
    char ch =;
    // Serial.print("ch = ");
    // Serial.println(ch);
    int led = ch - '0';

    // Serial.print("led = ");
    // Serial.println(led);

    // Set Color of LED based on how many fails in a row
    //RED = 52(d)
    //ORANGE = 53(e)
    //YELLOW = 54(f)
    //YELLOW-GREEN = 55(g)
    //GREEN = 56(h)
    //BLACK = 57(i)

    switch (led) {
      // Set color to RED
      case 52: {
          r = INTENSITY;
          g = 0;
          b = 0;

      // Set color to ORANGE
      case 53: {
          r = INTENSITY;
          g = (INTENSITY / 2);
          b = 0;

      // Set color to YELLOW
      case 54: {
          r = INTENSITY;
          g = INTENSITY;
          b = 0;

      // Set color to YELLOW-GREEN
      case 55: {
          r = (INTENSITY / 2);
          g = INTENSITY;
          b = 0;

      // Set color to GREEN
      case 56: {
          r = 0;
          g = INTENSITY;
          b = 0;

      // Set color to BLACK
      case 57: {
          r = 0;
          g = 0;
          b = 0;

      // To save on code, if we receive a 0 through a 9, turn on that
      // number of LED's
      case 0 ... 9:
        for (i = 0; i < led; i++) {
          pixels.setPixelColor(i, pixels.Color(r, g, b));

      // If we receive an "a", turn on 10 LED's
      case 49:
        for (i = 0; i < 10; i++) {
          pixels.setPixelColor(i, pixels.Color(r, g, b));

      // If we receive a "b", turn on 11 LED's
      case 50:

        for (i = 0; i < 11; i++) {
          pixels.setPixelColor(i, pixels.Color(r, g, b));

      // If we receive a "c", turn on 12 LED's
      case 51:

        for (i = 0; i < 12; i++) {
          pixels.setPixelColor(i, pixels.Color(r, g, b));

      // For testing, insert a delay if we see a ,
      case -4:
        delay(delayVal * 10);

        // if nothing else matches, do the default
        // default is optional
    // I had to add this bit of code to fix a problem where the Arduino buffer
    // apparently filled up after a very short time.  It would set the LED's on
    // but then pause for 2-3 seconds before it would receive the next command.
    // This tells the Arduino to flush out the buffer immediately.

If it’s not already evident, I’m not very adept at either Python or Arduino coding. I’m just starting out. The most frustrating thing for me is stumbling across syntax issues with code. 9 times out of 10, I know it’s possible to do something but I just can’t get the syntax right or use the correct modules. All this comes with time so maybe in a year, this code would be half or 1/3 the size it is right now.

Once you have everything installed and tested (you can turn on and off the LED’s), you have to connect the Pi to your network. I would consider this device to be a single purpose device and not put anything else on it that could interfere with the script and timing. They’re cheap enough that you should be able to justify this.


You can find this code on Github at  Please feel free to make modifications and generate a pull request- I’m always looking for a better mousetrap!


I hope this has helped you even a little bit. I had a great time setting it up and I look forward to making enhancements. The first one will be to indicate current upstream and downstream throughput using white and blue LED’s basically overlaid on the top of the latency indicators. Wish me luck!!

Internet Ping Meter (part 1 of 2)


How many of you “home IT support technicians” have heard this before?  I hear it a lot, so I decided to create a device that would notify me visually when problems occur.  Sometimes it winds up being a flaky wifi router that either reboots or just needs to take a breath.  Other times, it’s our Comcast connection in which case I can’t do anything other than call and file an outage.  The kids seem to have a hard time with understanding that even though I’ve explained it to them a hundred times.

A little background on the reason for this project.  At the company I work for, we employ a WAN load balancer which uses a series of pings to major internet presences such as google, AT&T or OpenDNS servers.  Basically the device pings each of those addresses once per second and based on specific criteria, can determine if one of the two internet connections is down and can take appropriate action.

This is what made me decide to develop my version of the ping meter.  There are a number of projects like this for the raspberry pi that involve some sort of visual representation.  I wanted to put together a project that incorporated both the raspberry pi, an arduino board and the NeoPixel ring.  This was mainly a project for me to learn how to integrate multiple devices.  Honestly I could probably have done this without the Arduino but I wanted to challenge myself a little.

At this point, I have the device working the way I want.  My next challenge is to package the device into something more aesthetically pleasing.  WAF (Wife Acceptance Factor) is an important aspect to any geek project like this if it’s gonna be displayed somewhere that’s visible.  I’m thinking maybe a small picture frame or maybe some sort of glass object that looks nice.

Here is a list of the parts you’ll need:

  • Raspberry Pi (any model should work)
  • Arduino board (I used an UNO but even that is overkill)
  • NeoPixel LED ring (12 LED segments)
  • Micro SD card (at least 4gb)
  • USB Type A to USB type B (printer/scanner cable)
  • 5V Micro USB power source (iPad charging brick is perfect)

I haven’t tested using a Pi Zero yet but I don’t see why it wouldn’t work.  I also have an Arduino Trinket (5v version) that I’m trying to use for this however out of the box it doesn’t support serial communication.  For size reasons, this combination would be perfect for just about any implementation where room is an issue.  You could just as easily use a larger NeoPixel ring or even a strip with some very minor code modifications.

There are two programs that are used to make this system work.  One is the “firmware” that you load onto the Arduino board itself.  The other is the python script that runs on the Pi.  Basically I use the Pi to ping 3 different IP addresses, and use the NeoPixel ring to display the average ping latency in LED segments.  If I can’t ping all three then I start to progressively change the color of the LED’s from green to red.  Throughout this project I learned a lot about programming in python, Arduino and interacting with external physical devices.  I first started by just getting the LED’s to turn on and off.  I borrowed a lot of code from examples and implemented the same routines to get the NeoPixel to do what I wanted to.

I tried to sprinkle comments throughout the code to explain what I’m doing and why.  Most of these were added after I made a breakthrough in something that was kicking my ass for awhile so I would know how to fix the problem the next time around.  I won’t focus a lot on how to install the OS on your Pi or how to download code to the Arduino- there are a LOT of helpful resources on the internet that can walk you through it.  Also, in the spirit of this being a learning exercise for me- I think it’s valuable for someone starting out fresh to do the research and have a basic understanding of what’s going on rather than just copying and pasting code.  If you’re trying to put this together and run into problems, feel free to comment on the article and I’ll do my best to answer questions.

In the next article, I’ll show you the code and how it all works.  Stay tuned!

Using VVOLs with vSphere 6 and Nimble

VMware Virtual Volumes is a concept that represents a major paradigm shift from the way storage is used in VMware today.

Below is a short 5 minute video that explains the basic concept of VVOLs.


Additionally, knowing the difference between communicating with LUNs as in the old world and communicating with PEs (Protocol Endpoints) is crucial to understanding what VVOLs brings to the table and why.

In short, PE’s are actually a special LUN on the storage array that the ESXi server uses to communicate with the array.  It’s not a LUN in the traditional sense, but more like a logical gateway to talk to the array.  I would say in some ways it’s similar in function to a gatekeeper LUN on an EMC array.  That LUN in turn maps to multiple sub-luns that make up the VM’s individual storage related components (vmdk, vswp, vmsd, vmsn etc).  When the host wants to talk to a LUN, it sends the request to the address of the PE “LUN” with an offset address of the actual LUN on the storage array.  Two things immediately came to mind once I understood this concept:

  1. Since all communication related to the sub-volumes is a VASA function, what happens when vCenter craps the bed?
  2. If I only have 1 PE, isn’t that going to be a huge bottleneck for storage I/O?

The answers to these and other questions are handily dealt with in a post here by VMware vExpert Paul Meehan.  Again- the short version is that vCenter is not needed after the host boots and gets information on PE’s and address offsets.  When it IS needed however is during a host boot.  Secondly, I/O traffic actually goes through the individual volumes, not the PE.  Remember, the PE is a logical LUN that serves as a map to the actual volumes underneath.

This brings me to the next video- understanding PEs.  This link starts about 12 minutes into an hour long presentation where PE’s are talked about.  Feel free to watch the entire video if you want to learn more!


Finally, let’s walk through how to set up VVOLs on your Nimble array.  There are a few pre-requisites before you can start:

  • NOS version 3.x or newer
  • vSphere 6.x or newer

Here’s the step by step process:

  1. Connect to web interface of local array
  2. Click on Administration -> VMware integration
  3. Fill in the following information
    • vCenter Name (this can be a vanity name- doesn’t have to be the address of the host)
    • choose the proper subnet on your Nimble array to communicate with vCenter
    • vCenter Host (FQDN or IP address)
    • Credentials
    • Check Web Client and VASA Provider
    • Click Save (This registers vCenter with the storage array and installs the VASA 2.0 provider)
  4. Navigate to Manage -> Storage Pools
  5. Select the Pool in which you want to create the VVOLs (for most sites this will be default)
  6. Click New Folder
  7. Change the Management Type to VMware Virtual Volumes
  8. Give the folder a Name and Description
  9. Set the size of the folder
  10. Choose the vCenter that you registered above, then click Create

Now you have a storage container on the Nimble array that you can use to create VVOLs.  Let’s look at the VMware side now:

  1. Connect to the vSphere web client for your vCenter 6 instance (this will not work with the thick client)
  2. Navigate to Storage and highlight your ESX server
  3. Click on Datastores on the right side of the window
  4. Click on the icon to create a new datastore
  5. Select your location (datacenter) then click next
  6. Select type VVOL then click next
  7. You should see at least one container- click next.  If not, try rescanning your HBA’s in the web client and start again
  8. Assign which host(s) will need access to the VVOL and click next
  9. On the summary screen- click finish

You should now see a new datastore.  Now let’s create a VM in the datastore and see what it looks like in the Nimble web interface!

  1. In vCenter, navigate to hosts and clusters
  2. Right click on your host to create a new virtual machine
  3. Click next under creation type to create a new virtual machine
  4. Give the VM a name, select the location where it should be created and click next
  5. Select the VVOL no requirements policy under VM storage policy
  6. Select the VVOL datastore that is compatible and click next
  7. Select ESXI 6.0 and later under the VM compatibility dtrop down and click next
  8. Choose the appropriate guest OS family and version then click next
  9. Adjust the virtual hardware to meet your needs and click next
  10. At the summary screen, verify all settings are correct and click Finish

Now if you navigate to Manage volumes in your Nimble web interface you will see multiple volumes for each VM you created.  Instead of putting all the .vmdk, .vmx, .vswp and other files inside a single datastore on a single LUN, each object is it’s own volume.  This is what allows you to set performance policies on a per VM basis because each volume can be treated differently.  You can set high performance policy on your production VM’s and low performance on dev/test for example.  Normally you would have to split your VMs into separate datastores and manage the performance policies on a per datastore level.  The problem with this is that you still have no visibility into each VM in that datastore at the storage layer.  With VVOLs, you can see latency, throughput and even noisy neighbor information on a per VM basis in the Nimble web interface!


Windows Wifi troubleshooting tools

Have you ever tried connecting your laptop to a Wifi network and for one reason or another it failed?  It can be extremely frustrating, even to a seasoned vet who knows their way around Windows.  The big problem is that you get virtually no information from the connection failure.  No logs, no error codes, nothing.

There is a reporting tool that is built into windows 8 and newer that will compile a very detailed report showing each connection attempt, its status and a ton of other stuff.  Here’s how to run the report and where it gets put:


  • Open a command prompt as administrator
  • Run the following command
    • netsh wlan show wlanreport
  • Note the path where the html file is generated.  It should be C:\ProgramData\Microsoft\Windows\WlanReport\wlan-report-latest.html
  • Open your favorite web browser and point it to that file.  voila!


Here’s a snippet of what some of the report looks like:



There is a LOT more information below this including extremely detailed information about all the network adapters on the system, the system itself and some script output.  If you click on the items in the session list above, it will bring you to a detailed log of that session and why it was able to or not able to connect.


Suffice it to say this is an invaluable tool to review logs and information all in one place.

ODA Patching – get ahead of yourself?

I was at a customer site deploying an X5-2 ODA.  They are standardizing on the patch level.  Even though is currently the latest, they don’t want to be on the bleeding edge.  Recall that the patch doesn’t include infrastructure patches (mostly firmware) so you have to install first, run the –infra patch to get the firmware and then update to


We unpacked the patch on both systems and then had an epiphany.  Why don’t we just unpack the patch as well and save some time later?  What could possibly go wrong?  Needless to say, when we went to install or even verify the patch it complained as follows:

ERROR: Patch version must be


Ok, so there has to be a way to clean that patch off the system so I can use right?  I stumbled across the oakcli manage cleanrepo command and thought for sure that would fix things up nicely.  Ran it and I got this output:


[root@CITX-5ODA-ODABASE-NODE0 tmp]# oakcli manage cleanrepo --ver
Deleting the following files...
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/OAK/
Deleting the files under /DOM0OAK/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/Seagate/ST95000N/SF04/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/Seagate/ST95001N/SA03/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/WDC/WD500BLHXSUN/5G08/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HGST/H101860SFSUN600G/A770/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/Seagate/ST360057SSUN600G/0B25/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HITACHI/H106060SDSUN600G/A4C0/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HITACHI/H109060SESUN600G/A720/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HITACHI/HUS1560SCSUN600G/A820/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HGST/HSCAC2DA6SUN200G/A29A/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HGST/HSCAC2DA4SUN400G/A29A/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/STEC/ZeusIOPs-es-G3/E12B/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/STEC/Z16IZF2EUSUN73G/9440/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Expander/ORACLE/DE2-24P/0018/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Expander/ORACLE/DE2-24C/0018/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Expander/ORACLE/DE3-24C/0291/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Controller/LSI-es-Logic/0x0072/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Controller/LSI-es-Logic/0x0072/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Ilom/SUN/X4370-es-M2/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HITACHI/H109090SESUN900G/A720/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/STEC/Z16IZF4EUSUN200G/944A/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HGST/H7240AS60SUN4.0T/A2D2/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HGST/H7240B520SUN4.0T/M554/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Disk/HGST/H7280A520SUN8.0T/P554/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Expander/SUN/T4-es-Storage/0342/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Controller/LSI-es-Logic/0x0072/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Controller/LSI-es-Logic/0x005d/4.230.40-3739/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Controller/LSI-es-Logic/0x0097/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Controller/Mellanox/0x1003/2.11.1280/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Ilom/SUN/X4170-es-M3/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Ilom/SUN/X4-2/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Ilom/SUN/X5-2/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/HMP/
Deleting the files under /DOM0HMP/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/IPMI/
Deleting the files under /DOM0IPMI/
Deleting the files under /JDK/1.7.0_91/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/ASR/5.3.1/Base
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/OPATCH/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/OPATCH/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/OPATCH/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/GI/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/DB/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/DB/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/DB/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/DB/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/OEL/6.7/Patches/6.7.1
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/OVM/3.2.9/Patches/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/OVS/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Controller/LSI-es-Logic/0x0072/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/thirdpartypkgs/Firmware/Controller/LSI-es-Logic/0x0072/
Deleting the files under $OAK_REPOS_HOME/pkgrepos/orapkgs/GI/


So I assumed that this fixed the problem.  Nope…


[root@CITX-5ODA-ODABASE-NODE0 tmp]# oakcli update -patch --verify

ERROR: Patch version must be



Ok so more searching the CLI manual and the oakcli help pages came up with bupkiss.  So I decided to do an strace of the oakcli command I had just ran.  As ususal- there was a LOT of garbage I didn’t care about or didn’t know what it was doing.  I did find however that it was reading the contents of a file that looked interesting to me:


[pid 5509] stat("/opt/oracle/oak/pkgrepos/System/VERSION", {st_mode=S_IFREG|0777, st_size=19, ...}) = 0
[pid 5509] open("/opt/oracle/oak/pkgrepos/System/VERSION", O_RDONLY) = 3
[pid 5509] read(3, "version=\n", 8191) = 19
[pid 5509] read(3, "", 8191) = 0
[pid 5509] close(3) = 0
[pid 5509] fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
[pid 5509] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f159799d000
[pid 5509] write(1, "\n", 1
) = 1
[pid 5509] write(1, "ERROR: Patch version must be 12."..., 40ERROR: Patch version must be
) = 40
[pid 5509] exit_group(0) = ?


There were a dozen or so lines after that, but I had what I needed.  Apparently /opt/oracle/oak/pkgrepos/System/VERSION contains the current version of the latest patch that has been unpacked.  The system software version is kept somewhere else because after I unpacked the patch, I ran an oakcli show version and it reported  But the VERSION file referenced earlier said  I assume when I unpacked the patch, it updates this file.  So what I wound up doing is changing the VERSION file back to as well as deleting the folder /opt/oracle/oak/pkgrepos/System/  Once I did this, everything worked as I expected.  I was able to verify and install the –infra portion of and continue on my merry way.


This highlights the fact that there isn’t a known way (to me at least) to delete an unpacked patch via oakcli or any python scripts I’ve been able to find yet.  Also- as an aside I tried just deleting the VERSION file assuming it would be rebuilt by oakcli and it didn’t.  I got this:


[root@CITX-5ODA-ODABASE-NODE0 System]# oakcli update -patch --verify
ERROR : Couldn't find the VERSION file to extract the current allowed version


So I just recreated the file and all was good.  I was hoping that the oak software didn’t maintain some sort of binary formatted database that kept track of all this information- I think I got lucky in this case.  Hope this helps someone out in a pinch!

Troubleshooting ODA Network connectivity

TroubleShootAudits1Setting up an ODA in a customer’s environment can either go very well or give you lots of trouble.  It all depends on having your install checklist completed, reviewed by the customer and any questions answered ahead of time.


I’ve installed dozens of ODA’s in a variety of configurations.  Ranging from a simple bare metal install to a complex virtualized install with multiple VMs and networks.  Now understand that I’m not a network engineer nor do I play one on TV, but I know enough about networking to have a civil conversation with a 2nd level network admin without getting too far out of my comfort zone. Knowing this- I can certainly appreciate the level of complexity involved in configuring and supporting an enterprise grade network.


Having said that, I find that when there are issues with a deployment, whether it’s an ODA, ZFS appliance, Exadata or other device, at least 80% of the time network misconfigurations are the culprit.  I can’t tell you how many times I’ve witnessed misconfigurations where the network admin swore up and down that they were set correctly but in fact were wrong.  It usually involves checking, re-checking and checking yet again to finally uncover the culprit.  Below, I’ll outline some of the snafu’s I’ve been involved with and the troubleshooting that can help resolve the issue.


Internet lock


  • Cabling: Are you sure the cables are all plugged into the right place?

Make sure that if you didn’t personally cable the ODA and you’re having network issues, don’t go too long without personally validating the cable configuration.  In this case, the fancy setup charts are a lifesaver!  On the X5-2 ODA’s for example, the InfiniBand private interconnect is replaced by the 10gb fiber ethernet option if the customer needs 10gb ethernet over fiber.  There is only one expansion slot available so unfortunately it’s either or.  As a result of this, the private interconnect is then facilitated by net0 and net1 with crossover cables (green and yellow) between the two compute nodes instead of the InfiniBand cables.  This can be missed very easily.  Also make sure the storage cables are all connected to the proper ports for your configuration- whether it’s one storage shelf or two.  This will typically be caught shortly after deploying the OS image whether it’s virtualized or bare metal.  There’s a storagetopology check that gets run during the install process that will catch most cabling mistakes but best not to chance it.

  • Switch configuration: Trunk port vs. Access port

When you configure a switch port, you need to tell the switch about what kind of traffic will pass through that port.  One of the important items is what network(s) does the server attached to this port need to talk on.  If you’re configuring a standalone physical server, chances are you won’t have a need to talk on more than one VLAN.  In this case, it’s usually appropriate to configure the switch port as an access port.  You can still put the server on a non-default VLAN (a VLAN other than 1) but the VLAN “tags” get stripped off at the switch and the server never sees them.

If however you’re setting up a VMware server or a machine that uses virtualization technology, it’s more likely that the VM’s that run on that server may indeed need to talk on more than one VLAN through the same network adapter(s).  In this case, you would need to set the port mode to trunked.  You then need to make sure to assign all the VLAN’s that the server will need to communicate on to that trunk port.  The server is then responsible for analyzing the VLAN tags and passing the traffic to the appropriate destination on the server.  This is one of the areas where the switch is usually configured incorrectly.  Most of the time, the network engineer fails to configure trunk mode on the port, forgets to assign the proper VLANs to the port or even setting a native VLAN on the port.

There is a difference between the default VLAN and a native VLAN.  The default VLAN is always present and is typically needed for intra-network device communication to take place.  Things like Cisco’s CDP protocol use this VLAN.  The Native VLAN, if configured, is treated similar to an access port from the perspective of the network adapter on the server.  The server NIC does not have to have a VLAN interface configured on top of it to be able to talk on the native VLAN.  If you want to talk on any other VLAN on this port however, you would need to configure a VLAN interface on the server to be able to receive those packets.  I’ve not seen the native VLAN used in a lot of configurations where more than one VLAN is needed, but it is most certainly a valid configuration.  Have the network team check these settings and make sure you understand how it should apply to your device.

  • Switch configuration: Aggregated ports vs. regular ports

Most switches have the ability to cobble together 2 to as many as 8 ports to provide higher throughput/utilization of the ports as well as redundancy at the same time.  This is referred to in different ways depending on your switch vendor.  Cisco calls it etherchannel, HP calls it Dynamic LACP trunking while extreme networks refer to it as sharing (LAG).  However you want to refer to it, it’s an implementation of a portion of the 802.3 IEEE standard which is commonly referred to as Link Aggregation or LACP (Link Aggregation Control Protocol).  Normally when you want to configure a pair of network interfaces on a server together, it’s usually to provide redundancy and avoid a SPOF (Single Point Of Failure).  I’ll refer to the standard Linux implementation mainly because I’m familiar with the different methods of load balancing that is typically employed.  This isn’t to say that other OS’s don’t have this capability (almost all do), I’m just not very experienced with all of them.

Active-Backup (Linux bonding driver mode=1) is a very simple implementation in which a primary interface is used for all traffic until that interface fails.  The traffic then moves over to the backup interface and communication is restored almost seamlessly.  There are other load balancing modes besides this one that don’t require any special configurations on the switch, each has their strengths and weaknesses.

LACP, which does require a specific configuration on the switch ports that are involved in order to work tends to be more performant while still maintaining redundancy.  The main reason for this is that there is out of band communication via the multicast group MAC address (01:80:c2:00:00:02) between the network driver on the server and the switch to keep both partners up to date on the status of the link.  This allows both ports to be utilized with an almost 50/50 split to evenly distribute the load between the totality of all the NICs in the LACP group effectively doubling (or better) throughput.

The reason I’m talking about this in the first place is because of the configuration that needs to be in place on the switch if you’re to use LACP.  If you configure your network driver for Active-Backup mode but the switch ports are set to LACP, you likely won’t see any packets at all on the server.  Likewise, if you have LACP configured on the server but the switch isn’t properly set up to handle it you’ll get the same result.  This is another setting that commonly gets misconfigured.  Other parameters such as STP (Spanning Tree Protocol), lacp_rate and passive vs. active LACP are some of the more common misconfigurations.  Also sometimes the configuration has to be split between two switches (again- no SPOF) and an MLAG configuration needs to be properly set up in order to allow LACP to work between switches.  Effectively, MLAG is one way of making two switches appear as one from a network protocol perspective and is required to span multiple switches within a LACP port group.  The take away here is to have the network admin verify their configuraiton on the switch(es) and ports involved.

  • Link speed: how fast can the server talk on the network?

Sometimes a server is capable of communicating at 10gb/s versus the more common 1gb/s either via copper or fiber media (most typically).  It used to be that you had to force switches to talk at 1gb/s in order for the server to negotiate that speed.  This was back when 1gb/s was newer and the handshake protocol that takes place between the NIC and the switch port at connection time was not as mature as it is now.  However, as a holdover from those halcyon days of yore, some network admins are prone to still set port speeds manually rather than letting them auto-negotiate like a good network admin should.  Thus you have servers connecting at 1gb/s when they should be running at 10gb/s.  Again- just something to keep in mind if you’re having speed issues.

  • Cable Quality: what speed is your cable rated at?

There are currently four common ratings for copper ethernet cables.  They are by no means the only ones but these are the most commonly used in datacenters.  They all have to do with how fast you can send data through the cables.  Cat 5 is capable of transmitting up to 1gb/s.  Cat 5e was an improvement on Cat 5 and introduced some enhancements that limited crosstalk (interference) between the 8 strands of a standard ethernet cable.  Cat 6 and 6a are further improvements on those standards, now allowing speeds of up to 10gb/s or more.  Basically the newer the Cat x number/letter the faster you can safely transmit data without data loss or corruption.  The reason I mention this is that I’ve been burned on more than one occasion when using cat5 for 1gb/s and had too much crosstalk which severely limited throughput and resulted in a lot of collisions.  Replacing the cable with a new cat 5 or higher rated cable almost always fixed the problem.  If you’re having communication problems, rule this out early on so you’re not chasing your tail in other areas.

  • IP Networking: Ensuring you have accurate network configurations

I’ve had a lot of problems in this area.  The biggest problem seems to be the fact that not all customers have taken the time to review and fill out the pre-install checklist.  This checklist prompts you for all the networking information you’ll need to do the install.  If you’ve been given IP information, before you tear your hair out make sure it’s correct.  I’ve been given multiple configurations at the same customer for the same appliance and each time there was something critical wrong that kept me from talking on the network.  Configuring VLAN’s can be especially trying because if you have it wrong, you just won’t see any traffic.  With regular non-VLAN configurations, If you put yourself on the wrong physical switch port or network, you can always sniff the network (tcpdump is now installed as part of the ODA software).  This doesn’t really work with VLAN traffic.  Other things to verify would be your subnet mask and default gateway.  If either of these are misconfigured, you’re gonna have problems.  Also as I mentioned earlier, don’t make the mistake of assuming you have to create a VLAN interface on the ODA just because you’re connected to a trunked port.  Remember the native VLAN traffic is passed on to the server with the VLAN tags stripped off so it uses a regular network interface (i.e. net1).

These are just some of the pitfalls you may encounter.  I hope some of this has helped!