How do you keep the Kolla Playing?

(With apologies to Bergman and Legrand)

I need to modify how the ipxe container mounts directories. Why? AARCH64 iPXE stuff. Specifically, I need to get my own version of a file into the directory that a container mounts when it is running. How do I do that? I don’t know yet, so I am going to look.

I do know that the starting point for running a container in Kolla is the Ansible playbook that launches it, and that for the Ironic containers at least, that calls into a custom library called kolla_docker. This is implemented as python executable:kolla-ansible/ansible/library/kolla_docker.py. The vast majority of that file, however, it parameter parsing, and the real work is done in the call to DockerWorker. While this has ansible as the first stage in the package name, it is actually under the kolla_ansible repository.

The call to this modules is a little convoluted. The module variable is of type AnsibleModule. After that:

dw = DockerWorker(module)
result = bool(getattr(dw, module.params.get('action'))())
module.exit_json(changed=dw.changed, result=result, **dw.result)

This is a way of getting an optional attribute off an object without throwing an exception. If the dw object has the action attribute, its value will be in the result variable, other wise result will be null. Hence, this is put in an exception handling block in case the action is bogus string that does not map to a function. The attribute returned from getattr is called as a no-args method on the dw object.

This could be thought of as a builder pattern, with the exit_json call being the build method. It seems the intention is to be more like the command pattern, but the command is built and executed in a single function.

In this case, we know that the “action” is "start_container" however, it is possible that there were previous calls to the same module prior in the workflow. Lets check:

$ grep kolla_docker ansible/roles/ironic/tasks/*
ansible/roles/ironic/tasks/bootstrap_service.yml:  kolla_docker:
ansible/roles/ironic/tasks/bootstrap_service.yml:  kolla_docker:
ansible/roles/ironic/tasks/bootstrap.yml:  kolla_docker:
ansible/roles/ironic/tasks/check-containers.yml:  kolla_docker:
ansible/roles/ironic/tasks/rolling_upgrade.yml:  kolla_docker:

bootstrap.yml calls import_tasks: bootstrap_service.yml, so we know the service call happens before any calls in bootstrap. An in both cases, the container started is part of the bootstrap process:

import_tasks: bootstrap_service.yml

name: "bootstrap_ironic"
name: "bootstrap_ironic_inspector"

At the end of the bootstrap_service.yaml file we can see the actual start of the ironic_pxe_container:

name: "bootstrap_ironic_pxe"

More interesting is when we look for grep -rn ironic_ipxe ansible/roles/* in this directory: that returns, amongst other things:

ansible/roles/ironic/defaults/main.yml:67:    container_name: ironic_ipxe

Looking at the defaults file, we can see that the set of services for ironic are defined in a declarative style of yaml, much like they were handled by Terraform. Here are the two services I care about, under the top level dictionary key of ironic_services.

  ironic-pxe:
    container_name: ironic_pxe
    group: ironic-pxe
    enabled: true
    image: "{{ ironic_pxe_image_full }}"
    volumes: "{{ ironic_pxe_default_volumes + ironic_pxe_extra_volumes }}"
    dimensions: "{{ ironic_pxe_dimensions }}"
  ironic-ipxe:
    container_name: ironic_ipxe
    group: ironic-ipxe
    # NOTE(mgoddard): This container is always enabled, since may be used by
    # the direct deploy driver.
    enabled: true
    image: "{{ ironic_pxe_image_full }}"
    volumes: "{{ ironic_ipxe_default_volumes + ironic_ipxe_extra_volumes }}"
    dimensions: "{{ ironic_ipxe_dimensions }}"
    healthcheck: "{{ ironic_ipxe_healthcheck }}"

The actual kolla_docker task that uses this is defined in a handler in ansible/roles/ironic/handlers/main.yml. While I don’t yet know what registers this handler, I know that it gets called at some point. It has an action of “recreate_or_restart_container”

Interesting to note that this seems to have changed in the master branch; there is no ipxe container, but rather dnsmasq and tftp containers as well as an ironic-http container.

My question now becomes: What happens when the action “recreate_or_restart_container” get executed by the DockerWorker module? We can see that the function is implemented here. The interesting work is delgated to the docker_client, stored in the member variable dc:

self.dc = get_docker_client()(**options)

At this point, I have all I need in order to understand how Kolla affects change on the Docker containers. I do not yet understand the Docker infrastructure that makes that happen in the context of systemd, but that is a tale for another day.

In my next post, I’ll go through the steps I need to take to change the way the ipxe container is run.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.