A bit about my day

I’ve been inspired by this post by one of the leadership team at the company I work for to talk about my working day.

  1. What time do you reach the office each day?
    Around 7:30. I typically work from home, so it’s largely adjusted by what is going on with the family before I get to my desk.
  2. Is your job varied?
    My work schedule is typically the similar sorts of tasks for a week or two, followed by documenting what I’ve done the previous couple of weeks, and then a new task for the next cycle. It means you get long enough to work on one thing before being distracted onto the next thing.
  3. Is your job creative?
    It requires creative and critical thinking to work out why things aren’t working the way you expect them to, or to fix issues for people you’ve agreed to help. It doesn’t typically involve understanding graphical design elements, user experience or visualising customer needs beyond what Visio brings.
  4. What do you spend the majority of your time doing?
    Investigating vendor products, writing Ansible playbooks, supporting consumers of my team’s output and documenting how I’ve integrated the vendor products into the environments I’m working on.
  5. Do you personalize your desk?
    I have a podcasting microphone, an Android plush toy, a lego fencer, two radio transceivers and a cardboard Millennium Falcon. Aside from that, it’s all business.
  6. Would you describe yourself as creative?
    Not particularly. I don’t really have an eye for graphical things and I’m not very good at writing prose. I can grammar check a document well though :)
  7. Do you have any quirky daily rituals?
    Not that I’m aware of. My wife probably disagrees!
  8. Do you tend to work on your own or with colleagues?
    I work from home and don’t work directly with anyone else on the projects I’ve been assigned, but have quite a few meetings with peers and managers during the week, so I don’t feel isolated or like I’m working by myself.
  9. How many hours on average do you work a day?
    I’m contracted for 37 hours a week, but I normally work between 7.5 to 8.5 hours each day. Overall, I’m probably doing about 40 hours. Oops!
  10. Roughly how much time do you spend each day on email? Taking calls? In meetings?
    Email is usually checked for an hour in the morning, then anything critical coming in during the day is handled as it comes in, so probably another 30 minutes or so then. I’m usually on two or three 30 minute online meetings during the day, and there’s probably three or four 15-45 minute calls. Depending on the complexity of the meetings and calls, I might be multi-tasking on other activities during those calls or meetings though.
  11. Do you use social media much for work?
    Not really. I tend to do more by email or phone.
  12. What do you enjoy most about your work?
    Every day is a new challenge, and I get lots of opportunities to help people – whether thats colleagues taking the output of my work, peers looking for guidance in areas I have experience, or being able to help vendors understand how we use their products.
  13. What type of music (if any) do you listen to at work?
    I use Google Music to create instant playlists based on what mood I’m in – usually starting from something like Iron Maiden, Metallica or Linkin Park. If I’m not listening to music, I’ll be listening podcasts, of which I have lots of feeds to select from, or to my local Amateur Radio repeater (GB3WP).
  14. What do you do for lunch?
    Working from home, I tend to eat with my wife and daughter (when she’s not at pre-school).
  15. Do you socialize with work colleagues?
    When we have team meetings, I’ll go out with colleagues.
  16. Are there any tasks (through your career) you’ve been especially glad to get rid of?
    No. My career has been pretty good to me – I started working in retail, specialising in radio products while I was at school and university, when I came back from uni, I went into an office, and learned a bit about sales and financials, and then ended up working for IT firms doing technical support, initially remote support, then on-site, and then for a helpdesk. I progressed from there into server support and then network security. The whole path has gone just right for me, changing at the point when things started to get stale or when I wanted a change. I’ve been exceptionally lucky!
  17. What is your last task of your day?
    Usually finishing up any unsent emails.
  18. How do you like to relax after work?
    I run a yearly conference and a monthly tech meetup. I watch TV with my wife and we play board games.
  19. Do you keep checking email through the evening?
    Nope. I used to, when I had an operational role, but now I don’t need to.
  20. Do you take work projects home with you?
    I work from home, so, technically yes. It’s rare that something from work intrudes into my home life though.
  21. What would you say to your 20-year old self?
    Nothing! 20 year-old me was a bit of an idiot, but without what he did, I wouldn’t be where I am today!
  22. If you could try out any job for a day, what would you choose?
    I like my job, so I don’t think I’d do anything else.
  23. What device did you use to answer these questions?
    Fujitsu E736 running Windows 10
  24. Do you use your own personal device for work?
    No.

Talk summary – Amateur (Ham) Radio – What’s it all about?

Format: Slide deck from a laptop standing next to a pillar in the open area. 10 attendees.

Slides: https://jon.sprig.gs/blog/post/slideshow/amateur-radio

Video: None

Slot: Day 2 (Sunday) Slot 4 12:30-12:55

Notes: Very quick summary of what you can do with Amateur Radio. Trying to encourage people to take up Amateur Radio from an Open Source mind set. Followed up with two attendees, one who was licensed, another who might get a license. Felt good :)

One to read: The 4 Core Capabilities of DevOps

One to read: “The 4 Core Capabilities of DevOps”

Sometimes, I don’t actually link to these articles for the text… sometimes I link to them for a single image. In this case, it’s absolutely because of the image at the end… (just before the advert for their course ;) )

A table, comparing 'Pathalogical Power-orientated' organisations, with 'Bureaucratic Rule-orientated' organisations, against 'Generative Performance-orientated' organisations.

Having worked in places with all three sets of attributes [1] this table is very interesting… I wonder what your organisation feels like to you, and what would it take to get you to a good place?

[1] “Modest cooperation”, “Messengers shot”, “Narrow responsibilities”, “Bridging tolerated”, “Failure leads to inquiry” and “Novelty crushed” was one of the more …. challenging places to work in, but the people were nice, so there’s that ;)

Setting UK keyboards in Vagrant Ubuntu Machines, using Ansible

Wow, now there’s a specific post title…

I use Ansible… quite a bit :) and one of the things I do with Ansible is to have a standard build desktop that I can create using Vagrant. Recently I upgraded the base to Ubuntu 18.04, and it annoyed me that I still didn’t have a working keyboard combination, so I kept getting US keyboards. I spent 20 minutes sorting it out, and here’s how to do it.

- name: Set keyboard layout
  debconf:
    name: "keyboard-configuration"
    question: "keyboard-configuration/{{ item.key }}"
    value: "{{ item.value }}"
    vtype: "{{ item.type|default('string') }}"
  with_items:
  - { key: "altgr", value: "The default for the keyboard layout", vtype: "select" }
  - { key: "compose", value: "No compose key", vtype: "select" }
  - { key: "ctrl_alt_bksp", value: "false", type: "boolean" }
  - { key: "variant", value: "English (UK)", vtype: "select" }
  - { key: "layout", value: "English (UK)", vtype: "select" }
  - { key: "model", value: "Generic 105-key PC (intl.)", vtype: "select" }

I posted how I got to this point over at the Server Fault post that got me most of the way. https://serverfault.com/a/912342/14832

Ansible Behaviour Change

For those of you who are working with #Ansible… Ansible 2.5 is out, and has an unusual documentation change around a key Ansible concept – `with_` loops Where you previously had:

with_dict: "{{ your_fact }}"
or
with_subelements:
- "{{ your_fact }}"
- some_subkey

This now should be written like this:

loop: "{{ lookup('dict', your_fact) }}"
and
loop: "{{ lookup('subelements', your_fact, 'some_subkey') }}"

Fear not, I hear you say, It’s fine, of course the documentation suggests that this is “how it’s always been”…… HA HA HA Nope. This behaviour is new as of 2.5, and needs ansible to be updated to the latest version. As far as I can tell, there’s no way to indicate to Ansible “Oh, BTW, this needs to be running on 2.5 or later”… so I wrote a role that does that for you.

ansible-galaxy install JonTheNiceGuy.version-check

You’re welcome :)

More useful URLs:

One to listen to: “And we’re in”

https://hackablepodcast.com/#/episodes/and-were-in

If you’ve ever wondered why you’re encouraged to use different passwords on every website, here’s a perfect example. In this episode from Cybersecurity Firm McAfee, a not-very-technical presenter asks a Penetration Tester (someone who is paid to breach a client’s own security to prove where it’s weaknesses are) to show how easy or hard it is to get into his accounts… In the end the tester goes after this presenter’s Dad’s account… and gets into his Amazon account and his Facebook account in only a couple of minutes.

He also explains some things you can do to keep an eye on these things for yourself. In general this is a fantastic podcast to listen to, and I’d strongly suggest you subscribe to it because it’s not too over-the-top, it’s not pitched at the techno-nerds (like me ;) ) it’s just … right.

Creating OpenStack “Allowed Address Pairs” for Clusters with Ansible

This post came about after a couple of hours of iterations, so I can’t necessarily quote all the sources I worked from, but I’ll do my best!

In OpenStack (particularly in the “Kilo” release I’m working with), if you have a networking device that will pass traffic on behalf of something else (e.g. Firewall, IDS, Router, Transparent Proxy) you need to tell the virtual NIC that the interface is allowed to pass traffic for other IP addresses, as OpenStack applies by default a “Same Origin” firewall rule to the interface. Defining this in OpenStack is more complex than it could be, because for some reason, you can’t define 0.0.0.0/0 as this allowed address pair, so instead you have to define 0.0.0.0/1 and 128.0.0.0/1.

Here’s how you define those allowed address pairs (note, this assumes you’ve got some scaffolding in place to define things like “network_appliance”):

allowed_address_pairs: "{% if (item.0.network_appliance|default('false')|lower() == 'true') or (item.1.network_appliance|default('false')|lower() == 'true') %}[{'ip_address': '0.0.0.0/1'}, {'ip_address': '128.0.0.0/1'}]{% else %}{{ item.0.allowed_address_pairs|default(omit) }}{% endif %}"

OK, so we’ve defined the allowed address pairs! We can pass traffic across our firewall. But (and there’s always a but), the product I’m working with at the moment has a floating MAC address in a cluster, when you define an HA pair. They have a standard schedule for how each port’s floating MAC is assigned… so here’s what I’ve ended up with (and yes, I know it’s a mess!)

allowed_address_pairs: "{% if (item.0.network_appliance|default('false')|lower() == 'true') or (item.1.network_appliance|default('false')|lower() == 'true') %}[{'ip_address': '0.0.0.0/1'},{'ip_address': '128.0.0.0/1'}{% if item.0.ha is defined and item.0.ha != '' %}{% for vdom in range(0,40, 10) %},{'ip_address': '0.0.0.0/1','mac_address': '{{ item.0.floating_mac_prefix|default(item.0.image.floating_mac_prefix|default(floating_mac_prefix)) }}:{% if item.0.ha.group_id|default(0) < 16 %}0{% endif %}{{ '%0x' | format(item.0.ha.group_id|default(0)|int) }}:{% if vdom+(item.1.interface|default('1')|replace('port', '')|int)-1 < 16 %}0{% endif %}{{ '%0x' | format(vdom+(item.1.interface|default('1')|replace('port', '')|int)-1) }}'}, {'ip_address': '128.0.0.0/1','mac_address': '{{ item.0.floating_mac_prefix|default(item.0.image.floating_mac_prefix|default(floating_mac_prefix)) }}:{% if item.0.ha.group_id|default(0) < 16 %}0{% endif %}{{ '%0x' | format(item.0.ha.group_id|default(0)|int) }}:{% if vdom+(item.1.interface|default('0')|replace('port', '')|int)-1 < 16 %}0{% endif %}{{ '%0x' | format(vdom+(item.1.interface|default('1')|replace('port', '')|int)-1) }}'}{% endfor %}{% endif %}]{% else %}{{ item.0.allowed_address_pairs|default(omit) }}{% endif %}"

Let's break this down a bit. The vendor says that each port gets a standard prefix, (e.g. DE:CA:FB:AD) then the penultimate octet is the "Cluster ID" number in hex, and then the last octet is the sum of the port number (zero-indexed) added to a VDOM number, which increments in 10's. We're only allowed to assign 10 "allowed address pairs" to an interface, so I've got the two originals (which are assigned to "whatever" the defined mac address is of the interface), and four passes around. Other vendors (e.g. this one) do things differently, so I'll probably need to revisit this once I know how the next one (and the next one... etc.) works!

So, we have here a few parts to make that happen.

The penultimate octet, which is the group ID in hex needs to be two hex digits long, and without adding more python modules to our default machines, we can't use a "pad" filter (to add 0's to the beginning of the mac octets), so we do that by hand:

{% if item.0.ha.group_id|default(0) < 16 %}0{% endif %}

And here's how to convert the group ID into a hex number:

{{ '%0x' | format(item.0.ha.group_id|default(0)|int) }}

Then the next octet is the sum of the VDOM and PortID. First we need to loop around the VDOMs. We don't always know whether we're going to be adding VDOMs until after deployment has started, so here we will assume we've got 3 VDOMs (plus VDOM "0" for management) as it doesn't really matter if we don't end up using them. We create the vdom variable like this:

{% for vdom in range(0, 40, 10) %} STUFF {% endfor %}

We need to put the actual port ID in there too. As we're using a with_subelement loop we can't create an increment, but what we can do is ensure we're recording the interface number. This only works here because the vendor has a sequential port number (port1, port2, etc). We'll need to experiment further with other vendors! So, here's how we're doing this. We already know how to create a hex number, but we do need to use some other Jinja2 filters here:

{{ '%0x' | format(vdom+(item.1.interface|default('1')|replace('port', '')|int)-1) }}

Let's pull this apart a bit further. item.1.interface is the name of the interface, and if it doesn't exist (using the |default('1') part) we replace it with the string "1". So, let's replace that variable with a "normal" value.

{{ '%0x' | format(vdom+("port1"|replace('port', '')|int)-1) }}

Next, we need to remove the word "port" from the string "port1" to make it just "1", so we use the replace filter to strip part of that value out. Let's do that:

{{ '%0x' | format(vdom+("1"|int)-1) }}

After that, we need to turn the string "1" into the literal number 1:

{{ '%0x' | format(vdom+1-1) }}

We loop through vdom several times, but let's pick one instance of that at random - 30 (the fourth iteration of the vdom for-loop):

{{ '%0x' | format(30+1-1) }}

And then we resolve the maths:

{{ '%0x' | format(30) }}

And then the |format(30) turns the '%0x' into the value "1e"

Assuming the vendor prefix is, as I mentioned, 'de:ca:fb:ad:' and the cluster ID is 0, this gives us the following resulting allowed address pairs:

[
{"ip_address": "0.0.0.0/1"},
{"ip_address": "128.0.0.0/1"},
{"ip_address": "0.0.0.0/1", "mac_address": "de:ca:fb:ad:00:00"},
{"ip_address": "128.0.0.0/1", "mac_address": "de:ca:fb:ad:00:00"},
{"ip_address": "0.0.0.0/1", "mac_address": "de:ca:fb:ad:00:0a"},
{"ip_address": "128.0.0.0/1", "mac_address": "de:ca:fb:ad:00:0a"},
{"ip_address": "0.0.0.0/1", "mac_address": "de:ca:fb:ad:00:14"},
{"ip_address": "128.0.0.0/1", "mac_address": "de:ca:fb:ad:00:14"},
{"ip_address": "0.0.0.0/1", "mac_address": "de:ca:fb:ad:00:1e"},
{"ip_address": "128.0.0.0/1", "mac_address": "de:ca:fb:ad:00:1e"}
]

I hope this has helped you!

Sources of information:

Defining Networks with Ansible

In my day job, I’m using Ansible to provision networks in OpenStack. One of the complaints I’ve had about the way I now define them is that the person implementing the network has to spell out all the network elements – the subnet size, DHCP pool, the addresses of the firewalls and names of those items. This works for a manual implementation process, but is seriously broken when you try to hand that over to someone else to implement. Most people just want something which says “Here is the network I want to implement – 192.0.2.0/24″… and let the system make it for you.

So, I wrote some code to make that happen. It’s not perfect, and it’s not what’s in production (we have lots more things I need to add for that!) but it should do OK with an IPv4 network.

Hope this makes sense!

---
- hosts: localhost
  vars:
  - networks:
      # Defined as a subnet with specific router and firewall addressing
      external:
        subnet: "192.0.2.0/24"
        firewall: "192.0.2.1"
        router: "192.0.2.254"
      # Defined as an IP address and CIDR prefix, rather than a proper network address and CIDR prefix
      internal_1:
        subnet: "198.51.100.64/24"
      # A valid smaller network and CIDR prefix
      internal_2:
        subnet: "203.0.113.0/27"
      # A tiny CIDR network
      internal_3:
        subnet: "203.0.113.64/30"
      # These two CIDR networks are unusable for this environment
      internal_4:
        subnet: "203.0.113.128/31"
      internal_5:
        subnet: "203.0.113.192/32"
      # A massive CIDR network
      internal_6:
        subnet: "10.0.0.0/8"
  tasks:
  # Based on https://stackoverflow.com/a/47631963/5738 with serious help from mgedmin and apollo13 via #ansible on Freenode
  - name: Add router and firewall addressing for CIDR prefixes < 30     set_fact:       networks: >
        {{ networks | default({}) | combine(
          {item.key: {
            'subnet': item.value.subnet | ipv4('network'),
            'router': item.value.router | default((( item.value.subnet | ipv4('network') | ipv4('int') ) + 1) | ipv4),
            'firewall': item.value.firewall | default((( item.value.subnet | ipv4('broadcast') | ipv4('int') ) - 1) | ipv4),
            'dhcp_start': item.value.dhcp_start | default((( item.value.subnet | ipv4('network') | ipv4('int') ) + 2) | ipv4),
            'dhcp_end': item.value.dhcp_end | default((( item.value.subnet | ipv4('broadcast') | ipv4('int') ) - 2) | ipv4)
          }
        }) }}
    with_dict: "{{ networks }}"
    when: item.value.subnet | ipv4('prefix') < 30   - name: Add router and firewall addressing for CIDR prefixes = 30     set_fact:       networks: >
        {{ networks | default({}) | combine(
          {item.key: {
            'subnet': item.value.subnet | ipv4('network'),
            'router': item.value.router | default((( item.value.subnet | ipv4('network') | ipv4('int') ) + 1) | ipv4),
            'firewall': item.value.firewall | default((( item.value.subnet | ipv4('broadcast') | ipv4('int') ) - 1) | ipv4)
          }
        }) }}
    with_dict: "{{ networks }}"
    when: item.value.subnet | ipv4('prefix') == 30
  - debug:
      var: networks