<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Blogs on Stephen Finucane (Fin-oo-can)</title>
    <link>https://that.guru/blog/</link>
    <description>Recent content in Blogs on Stephen Finucane (Fin-oo-can)</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-IE</language>
    <lastBuildDate>Mon, 05 Jan 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://that.guru/blog/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Are We Typed Yet?</title>
      <link>https://that.guru/blog/are-we-typed-yet/</link>
      <pubDate>Mon, 05 Jan 2026 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/are-we-typed-yet/</guid>
      <description>&lt;p&gt;This is a living document that describes the status of typing in various Python packages in the OpenStack system.
Currently this document focuses on libraries and clients. While many services have introduced some degree of typing,
none are complete and they are not intended to be consumed by others, thus they are not documented here.&lt;/p&gt;
&lt;p&gt;We determine whether a package is typed or not by looking for two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;py.typed&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;This is what Python itself uses to determine whether to consume type hints from the library. If this file is
present, the package is considered at least partially typed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use of &lt;a href=&#34;https://mypy.readthedocs.io/en/stable/getting_started.html#strict-mode-and-configuration&#34;&gt;mypy&amp;rsquo;s strict mode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;mypy is currently the only type checker in use across OpenStack projects. If &lt;code&gt;[tool.mypy] strict&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;
in a package&amp;rsquo;s &lt;code&gt;pyproject.toml&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; the &lt;code&gt;py.typed&lt;/code&gt; file is present, then the library is considered &lt;em&gt;fully&lt;/em&gt; typed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;Last updated March 7, 2026&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;oslo-libraries&#34;&gt;Oslo libraries&lt;/h2&gt;

  
    &lt;table&gt;
      &lt;thead&gt;
        &lt;tr&gt;
          &lt;th&gt;Package&lt;/th&gt;
          &lt;th&gt;Typed?&lt;/th&gt;
        &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/automaton&#34;&gt;automaton&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (unreleased)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/castellan&#34;&gt;castellan&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/debtcollector&#34;&gt;debtcollector&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (unreleased)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/etcd3gw&#34;&gt;etcd3gw&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/futurist&#34;&gt;futurist&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/microversion-parse&#34;&gt;microversion-parse&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 2.1.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/openstackdocstheme&#34;&gt;openstackdocstheme&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (unreleased)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/os-api-ref&#34;&gt;os-api-ref&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (unreleased)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.cache&#34;&gt;oslo.cache&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 4.1.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.concurrency&#34;&gt;oslo.concurrency&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 7.3.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.config&#34;&gt;oslo.config&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.context&#34;&gt;oslo.context&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 6.3.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.db&#34;&gt;oslo.db&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.i18n&#34;&gt;oslo.i18n&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 6.7.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.limit&#34;&gt;oslo.limit&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 2.9.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.log&#34;&gt;oslo.log&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 8.0.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.messaging&#34;&gt;oslo.messaging&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.metrics&#34;&gt;oslo.metrics&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 0.15.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.middleware&#34;&gt;oslo.middleware&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 7.0.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.policy&#34;&gt;oslo.policy&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 5.0.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.privsep&#34;&gt;oslo.privsep&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 3.10.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.reports&#34;&gt;oslo.reports&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.rootwrap&#34;&gt;oslo.rootwrap&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 7.9.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.serialization&#34;&gt;oslo.serialization&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 5.9.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.service&#34;&gt;oslo.service&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.upgradecheck&#34;&gt;oslo.upgradecheck&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 2.7.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.utils&#34;&gt;oslo.utils&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 9.2.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.versionedobjects&#34;&gt;oslo.versionedobjects&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslo.vmware&#34;&gt;oslo.vmware&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/oslotest&#34;&gt;oslotest&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (unreleased)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/osprofiler&#34;&gt;osprofiler&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/sphinx-feature-classification&#34;&gt;sphinx-feature-classification&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (unreleased)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/stevedore&#34;&gt;stevedore&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 5.6.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/taskflow&#34;&gt;taskflow&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/tooz&#34;&gt;tooz&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/whereto&#34;&gt;whereto&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (unreleased)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
      &lt;/tbody&gt;
    &lt;/table&gt;

    
    
    
      
    
      
    
      
    
      
    
      
    
      
    
      
    
    
    
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;The following packages have been excluded:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;cookiecutter (a cookiecutter template)&lt;/li&gt;&lt;li&gt;devstack-plugin-amqp1 (a devstack plugin)&lt;/li&gt;&lt;li&gt;devstack-plugin-kafka (a devstack plugin)&lt;/li&gt;&lt;li&gt;openstack-doc-tools (docs-only)&lt;/li&gt;&lt;li&gt;oslo-cookiecutter (a cookiecutter template)&lt;/li&gt;&lt;li&gt;oslo-specs (docs-only)&lt;/li&gt;&lt;li&gt;oslo.tools (deprecated)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;/aside&gt;
    
  


&lt;h2 id=&#34;sdks-and-clients&#34;&gt;SDKs and Clients&lt;/h2&gt;

  
    &lt;table&gt;
      &lt;thead&gt;
        &lt;tr&gt;
          &lt;th&gt;Package&lt;/th&gt;
          &lt;th&gt;Typed?&lt;/th&gt;
        &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/cliff&#34;&gt;cliff&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 4.11.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/keystoneauth&#34;&gt;keystoneauth&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 5.13.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/openstacksdk&#34;&gt;openstacksdk&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ⚠️ (partial, since 0.37.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/os-service-types&#34;&gt;os-service-types&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 1.8.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/osc-lib&#34;&gt;osc-lib&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              
                ✅ (since 4.2.0)
              
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
        &lt;tr&gt;
          &lt;td&gt;&lt;a href=&#34;https://pypi.org/project/python-openstackclient&#34;&gt;python-openstackclient&lt;/a&gt;&lt;/td&gt;
          &lt;td&gt;
            
              ❌
            
          &lt;/td&gt;
        &lt;/tr&gt;
        
      &lt;/tbody&gt;
    &lt;/table&gt;

    
    
    
      
    
      
    
      
    
    
    
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;The following packages have been excluded:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;codegenerator (not intended for external consumption)&lt;/li&gt;&lt;li&gt;openapi (not intended for external consumption)&lt;/li&gt;&lt;li&gt;os-client-config (deprecated)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;/aside&gt;
    
  


</description>
    </item>
    
    <item>
      <title>Glance Tasks API</title>
      <link>https://that.guru/blog/glance-tasks-api/</link>
      <pubDate>Fri, 15 Aug 2025 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/glance-tasks-api/</guid>
      <description>&lt;p&gt;One the joys (or miseries) of working on OpenStack client tooling is that you get exposed to all the nuances of the
OpenStack API tooling. The microservice-like architecture, with projects being managed by often wholly separate teams,
means there&amp;rsquo;s precious little consistency across APIs, beyond that enforced by the long-dormant &lt;a href=&#34;https://specs.openstack.org/openstack/api-sig/&#34;&gt;API SIG&lt;/a&gt;. I&amp;rsquo;ve
touched on this before, covering things like the disparity in &lt;a href=&#34;https://that.guru/blog/api-versioning-in-openstack/&#34;&gt;API versioning&lt;/a&gt;, and its been particularly
notable during work on the &lt;a href=&#34;https://that.guru/blog/an-update-on-openapi-in-openstack/&#34;&gt;OpenAPI effort&lt;/a&gt;. OpenStack is also pretty old (at least in software terms) and most
service APIs come with at least a decade of baggage: there is an abundance of APIs that still exist in services but are
no longer relevant or useful in modern deployments.&lt;/p&gt;
&lt;p&gt;The Glance Tasks API is an example of one such API. It&amp;rsquo;s a positively ancient API, dating back to the &lt;a href=&#34;https://wiki.openstack.org/wiki/Glance-tasks-api&#34;&gt;Havana
release&lt;/a&gt; (2013), and has long-since been made redundant by the image import API introduced in the Mitaka
release (2016). Nonetheless, python-openstackclient (OSC) and openstacksdk (SDK) aim to support all OpenStack APIs, and
the tasks API, while deprecated, is still present in Glance in the Flamingo release, meaning OSC and SDK should support
them.&lt;/p&gt;
&lt;p&gt;As always, when adding support for a new API to SDK and OSC, its important that you figure out how that API actually
works, ideally by playing around with the API using a local DevStack: the OpenStack API docs are good, but they&amp;rsquo;re not
flawless, and if I had a euro for every time the API docs had said one thing only for the API to behave a different way,
I wouldn&amp;rsquo;t be stuck doing my own painting. Anywho, adding support for the tasks API is no different and I conducted a
few tests to prove things out while reviewing some recent patches against both SDK and OSC. Below are my test notes,
which I&amp;rsquo;m sharing in case anyone else ends up having to do something similar.&lt;/p&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;
&lt;p&gt;We start by creating a task. Because the tasks API is deprecated and replaced by the image import process, OSC has
indicated that it won&amp;rsquo;t add support for task creation. Thus, for this one we need to use either glanceclient or &lt;code&gt;curl&lt;/code&gt;.
I used the former, creating a sample task for an existing image. I provided syntactically valid-but-nonsense input since
I only wanted to test the behavior of the API; I didn&amp;rsquo;t actually need the task to complete:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ glance task-create --type &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;api_image_import&amp;#34;&lt;/span&gt; --input &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;image_id&amp;#34;: &amp;#34;8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;import_req&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;method&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      &amp;#34;name&amp;#34;: &amp;#34;copy-image&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;all_stores&amp;#34;: true,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;all_stores_must_succeed&amp;#34;: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;backend&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;fast&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;cheap&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;slow&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;reliable&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    &amp;#34;common&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once created, I was able to view this task in a few ways. Firstly, by listing all tasks. This is supported by OSC, so I
used that:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack image task list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------------------------+------------------+------------+----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| ID                                   | Type             | Status     | Owner                            |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------------------------+------------------+------------+----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 546641fe-6a35-4b0e-90d2-b45367fc94b2 | api_image_import | processing | d8dea803f1fe44e2bdf9d9d017750b4e |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------------------------+------------------+------------+----------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;or, via &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ token&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;openstack token issue -f value -c id&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ curl -s -X GET &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://10.0.111.148/image/v2/tasks&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Accept: application/json&amp;#34;&lt;/span&gt;-H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;X-Auth-Token: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;token&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | jq
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tasks&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;546641fe-6a35-4b0e-90d2-b45367fc94b2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;api_image_import&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;processing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;owner&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;d8dea803f1fe44e2bdf9d9d017750b4e&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-08-15T11:32:10Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;updated_at&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-08-15T11:32:10Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;self&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/v2/schemas/task&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;first&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/v2/tasks&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/v2/schemas/tasks&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I could also inspect the individual task:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack image task show 546641fe-6a35-4b0e-90d2-b45367fc94b2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Field      | Value                                                                                                                                                                                                                    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| created_at | 2025-08-15T11:32:10Z                                                                                                                                                                                                     |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| expires_at | None                                                                                                                                                                                                                     |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| id         | 546641fe-6a35-4b0e-90d2-b45367fc94b2                                                                                                                                                                                     |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| input      | &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;image_id&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;import_req&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;method&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;copy-image&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;all_stores&amp;#39;&lt;/span&gt;: True, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;all_stores_must_succeed&amp;#39;&lt;/span&gt;: False&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;backend&amp;#39;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;fast&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cheap&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;slow&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;reliable&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;common&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]}&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| message    |                                                                                                                                                                                                                          |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| owner_id   | d8dea803f1fe44e2bdf9d9d017750b4e                                                                                                                                                                                         |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| properties |                                                                                                                                                                                                                          |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| result     | None                                                                                                                                                                                                                     |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| status     | processing                                                                                                                                                                                                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| type       | api_image_import                                                                                                                                                                                                         |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| updated_at | 2025-08-15T11:32:10Z                                                                                                                                                                                                     |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;again, via &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ curl -s -X GET &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://10.0.111.148/image/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Accept: application/json&amp;#34;&lt;/span&gt;-H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;X-Auth-Token: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;token&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | jq
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;546641fe-6a35-4b0e-90d2-b45367fc94b2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;import_req&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;method&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;copy-image&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;all_stores&amp;#34;&lt;/span&gt;: true,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;all_stores_must_succeed&amp;#34;&lt;/span&gt;: false
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;backend&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fast&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cheap&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reliable&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;common&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;api_image_import&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;processing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;owner&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;d8dea803f1fe44e2bdf9d9d017750b4e&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result&amp;#34;&lt;/span&gt;: null,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-08-15T11:32:10Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;updated_at&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-08-15T11:32:10Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;self&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/v2/schemas/task&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;request_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;req-1918a068-0ce3-4eff-81f5-625e2d0037ae&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;105a84e11f0a421ea814833a5d23f37b&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, I could list tasks associated with the image. For reasons unbeknownst to me, glance opted to implement this
via a separate, nested API (&lt;code&gt;/images/{imageID}/tasks&lt;/code&gt;) rather than a new filter on the existing API
(&lt;code&gt;/tasks?image_id={imageID}&lt;/code&gt;). This is supposedly supported by glanceclient, but attempting to use this command with the
latest glanceclient version yielded the following error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ glance image-tasks 8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Server does not support image tasks API &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;v2.12&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This seemed odd since the API &lt;em&gt;should&lt;/em&gt; be supported. Sure enough, when I tried with &lt;code&gt;curl&lt;/code&gt;, things worked as expected:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ curl -s -X GET &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://10.0.111.148/image/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Accept: application/json&amp;#34;&lt;/span&gt;-H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; -H &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;X-Auth-Token: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;token&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; | jq
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;546641fe-6a35-4b0e-90d2-b45367fc94b2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;import_req&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;method&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;copy-image&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;all_stores&amp;#34;&lt;/span&gt;: true,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;all_stores_must_succeed&amp;#34;&lt;/span&gt;: false
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;backend&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fast&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cheap&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;slow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reliable&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;common&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;api_image_import&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;processing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;owner&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;d8dea803f1fe44e2bdf9d9d017750b4e&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;result&amp;#34;&lt;/span&gt;: null,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-08-15T11:32:10Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;updated_at&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-08-15T11:32:10Z&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;self&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/v2/tasks/546641fe-6a35-4b0e-90d2-b45367fc94b2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;schema&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/v2/schemas/task&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8bd3bebc-8e1b-40fe-a292-1a63a53dcfd9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;request_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;req-1918a068-0ce3-4eff-81f5-625e2d0037ae&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;105a84e11f0a421ea814833a5d23f37b&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Clearly a bug report is needed here.&lt;/p&gt;
&lt;p&gt;Finally, task deletion. There isn&amp;rsquo;t a task deletion API. Rather, tasks have an expiry date, after which glance will reap
the task. You can likely force this with &lt;code&gt;glance-manage&lt;/code&gt; but I didn&amp;rsquo;t care enough to do so.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Closer Look at the Cinder CSI Driver and the Topology Feature</title>
      <link>https://that.guru/blog/csi-drivers-and-openstack/</link>
      <pubDate>Thu, 03 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/csi-drivers-and-openstack/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve recently found myself once again working on the OpenStack Cinder CSI Driver and the Operator that OpenShift uses to
deploy this. This work has inspired me to improve my knowledge of how the Cinder CSI Driver - and CSI drivers in
general - work. Below is my current high-level understanding of both as well as a quick summary of changes we are making
to the Cinder CSI Driver Operator in OpenShift 4.19.&lt;/p&gt;
&lt;h2 id=&#34;deployment-of-the-cinder-csi-driver&#34;&gt;Deployment of the Cinder CSI Driver&lt;/h2&gt;
&lt;p&gt;The Cinder CSI Driver Operator deploys the driver itself as two components: a controller component and a per-node
component, which is the &lt;a href=&#34;https://kubernetes-csi.github.io/docs/deploying.html&#34;&gt;typical deployment model for CSI Drivers&lt;/a&gt;. The controller component is managed
via a Deployment which you can see &lt;a href=&#34;https://github.com/openshift/csi-operator/blob/release-4.18/assets/overlays/openstack-cinder/generated/standalone/controller.yaml&#34;&gt;here&lt;/a&gt;. It consists of the controller plugin and a number of sidecar
containers which interface between the controller and the Kubernetes controller manager (&lt;code&gt;kube-controller-manager&lt;/code&gt;) via
a Unix domain socket and handle different RPC calls. Breaking these down one-by-one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The controller plugin container (&lt;code&gt;csi-driver&lt;/code&gt;) implements the Controller Service and Identity Service set of RPCs
described in the &lt;a href=&#34;https://github.com/container-storage-interface/spec/blob/master/spec.md#rpc-interface&#34;&gt;CSI spec&lt;/a&gt;. It is responsible for handling requests by calling the cloud provider&amp;rsquo;s APIs
(Cinder and Nova, this case).&lt;/p&gt;
&lt;p&gt;You can find the Cinder CSI implementation of the Controller Service &lt;a href=&#34;https://github.com/kubernetes/cloud-provider-openstack/blob/release-1.32/pkg/csi/cinder/controllerserver.go&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The attacher sidecar container (&lt;code&gt;csi-attacher&lt;/code&gt;) watches for attach and detach calls and calls
&lt;code&gt;ControllerPublishVolume&lt;/code&gt; and &lt;code&gt;ControllerUnpublishVolume&lt;/code&gt;, respectively. (&lt;a href=&#34;https://github.com/kubernetes-csi/external-attacher&#34;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The provisioner sidecar container (&lt;code&gt;csi-provisioner&lt;/code&gt;) watches for PVC creation and deletion and calls &lt;code&gt;CreateVolume&lt;/code&gt;
and &lt;code&gt;DeleteVolume&lt;/code&gt;, respectively. (&lt;a href=&#34;https://github.com/kubernetes-csi/external-provisioner&#34;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The snapshotter sidecar container (&lt;code&gt;csi-snapshotter&lt;/code&gt;) does the same as the provisioner but for snapshots, calling
&lt;code&gt;CreateSnapshot&lt;/code&gt; and &lt;code&gt;DeleteSnapshot&lt;/code&gt;. (&lt;a href=&#34;https://github.com/kubernetes-csi/external-snapshotter&#34;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The resizer sidecar container (&lt;code&gt;csi-resizer&lt;/code&gt;) watches for changes to a PVC and calls &lt;code&gt;ControllerExpandVolume&lt;/code&gt; as
necessary. (&lt;a href=&#34;https://github.com/kubernetes-csi/external-resizer&#34;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The per-node component, by comparison, is deployed to each node using a DaemonSet. You can see the definition for this
&lt;a href=&#34;https://github.com/openshift/csi-operator/blob/release-4.18/assets/overlays/openstack-cinder/generated/standalone/node.yaml&#34;&gt;here&lt;/a&gt;. It consists of the node plugin and a single sidecar container:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The node plugin container (&lt;code&gt;csi-driver&lt;/code&gt;) implements the Node Service and Identity Service sets of RPCs described in
the &lt;a href=&#34;https://github.com/container-storage-interface/spec/blob/master/spec.md#rpc-interface&#34;&gt;CSI spec&lt;/a&gt;. It is responsible for reporting information about the node and for bind mounting volumes
once they are attached to the host. Specifically, it reports an ID of the node, the maximum number of volumes it
supports, and topology information. In the case of Cinder, both the ID and topology information are sourced from the
metadata service, while the volume limit is determined via a configuration option.&lt;/p&gt;
&lt;p&gt;You can find the Cinder CSI implementation of the Node Service &lt;a href=&#34;https://github.com/kubernetes/cloud-provider-openstack/blob/release-1.32/pkg/csi/cinder/nodeserver.go&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The node-driver-registrar sidecar container (&lt;code&gt;csi-node-driver-registrar&lt;/code&gt;) registers the CSI driver with kubelet,
allowing kubelet to call &lt;code&gt;NodeGetInfo&lt;/code&gt;, &lt;code&gt;NodeStageVolume&lt;/code&gt;, &lt;code&gt;NodePublishVolume&lt;/code&gt; etc. (&lt;a href=&#34;https://github.com/kubernetes-csi/node-driver-registrar&#34;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;changes-to-topology-auto-configuration&#34;&gt;Changes to topology auto-configuration&lt;/h2&gt;
&lt;p&gt;Now that we understand the various components that make up the CSI Driver, let&amp;rsquo;s take a look at the changes we&amp;rsquo;ve been
working on in this area. As I&amp;rsquo;ve &lt;a href=&#34;https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-2&#34;&gt;previously discussed&lt;/a&gt;, the Cinder CSI Driver has support for
Availability Zones (or, in CSI parlance, the CSI Topology Feature) and since OpenShift 4.16 or so the Cinder CSI Driver
Operator has supported auto-configuration of this feature. Without getting too into the weeds, the way this is
determined is via a simple set comparison: the set of Compute AZs is compared to the set of Block Storage AZs, and if
the former isn&amp;rsquo;t a subset of the latter (e.g. if there was a Compute AZ called &lt;code&gt;foo&lt;/code&gt; but no equivalent Block Storage AZ
of the same name) then we determine that the feature should be disabled. Once we&amp;rsquo;ve determined this, we toggle the
&lt;code&gt;Topology&lt;/code&gt; feature gate of the CSI Provisioner sidecar container, thus ensuring that the &lt;code&gt;AccessibilityRequirements&lt;/code&gt;
field of the &lt;code&gt;CreateVolumeRequest&lt;/code&gt; struct generated by the provisioner (and fed to the controller plugin) &lt;a href=&#34;https://github.com/kubernetes-csi/external-provisioner/blob/release-5.1/pkg/controller/controller.go#L682-L697&#34;&gt;would not be
populated&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, things change and the Topology feature is now considered mature and is enabled by default. This means it is
likely that the feature flag will be removed at some point in the not-too-distant future, which in turn means we need to
find another way to enable and disable topology support from the operator. The solution we&amp;rsquo;ve arrived at is to copy what
was done in Manila and add support for a new &lt;code&gt;--with-topology&lt;/code&gt; option to both the controller plugin and node plugin
services. This new option has different effects depending on where it is set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For the controller plugin, the option determines whether (a) the calls to Cinder include a requested AZ and (b)
whether the &lt;code&gt;CreateVolumeResponse&lt;/code&gt; returned by the &lt;code&gt;CreateVolume&lt;/code&gt; call includes topology accessibility information.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For the node plugin, the option determines whether the node reports (a) the capability (as part of the
&lt;code&gt;GetPluginCapabilities&lt;/code&gt; RPC) and (b) a topology information (as part of the &lt;code&gt;NodeGetInfo&lt;/code&gt; call).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This work has been implemented in &lt;a href=&#34;https://github.com/kubernetes/cloud-provider-openstack/pull/2743&#34;&gt;kubernetes/cloud-provider-openstack#2743&lt;/a&gt; (with some follow-ups in
&lt;a href=&#34;https://github.com/kubernetes/cloud-provider-openstack/pull/2862&#34;&gt;kubernetes/cloud-provider-openstack#2862&lt;/a&gt; and &lt;a href=&#34;https://github.com/kubernetes/cloud-provider-openstack/pull/2865&#34;&gt;kubernetes/cloud-provider-openstack#2865&lt;/a&gt;). With
the new option in place, we&amp;rsquo;ve been able to change how the Operator toggles the Topology feature. Now, instead of
enabling and disabling the feature gate on the &lt;code&gt;csi-provisioner&lt;/code&gt; container, it can enable and disable the feature on the
&lt;code&gt;csi-driver&lt;/code&gt; containers in the controller deployment and node daemonsets. &lt;em&gt;That&lt;/em&gt; work has been implemented in
&lt;a href=&#34;https://github.com/openshift/csi-operator/pull/345&#34;&gt;openshift/csi-operator#345&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next steps&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m hoping this is last time I feel the need to write about the Cinder CSI Driver and its Operator. The work we&amp;rsquo;ve done
here should future proof both and ensure that, barring major changes to the CSI Spec itself, few other changes will be
needed for the foreseeable. I would however like to get a better understanding of how the equivalent feature in the
Manila CSI Driver works, so watch our for a possible post on that topic down the line.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>An Update on OpenAPI in Openstack</title>
      <link>https://that.guru/blog/an-update-on-openapi-in-openstack/</link>
      <pubDate>Thu, 03 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/an-update-on-openapi-in-openstack/</guid>
      <description>&lt;p&gt;I previously presented on our work to bring OpenAPI to OpenStack as part of the 2024 OpenInfra Summit Asia, the slides
for which you can find &lt;a href=&#34;https://that.guru/talks/api-contracts-bringing-openapi-and-typing-to-openstack/&#34;&gt;here&lt;/a&gt;. Since that talk, another
release cycle has come and gone and our work has continued to progress. Below is a summary of the current &amp;ldquo;state of
play&amp;rdquo; for OpenAPI support in OpenStack and a reminder of our long-term goals in the area.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This information is accurate as of the start of the 2025.2 (Flamingo) cycle. I will attempt to update it over the course
of this cycle.&lt;/div&gt;
&lt;/aside&gt;


&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;Updated on 2025-04-15 to better describe the ongoing work in OSC and SDK.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;
&lt;p&gt;The work to add OpenAPI schemas can be broken into two parts: adding schemas to services and writing tooling to consume
these schemas and generate both documentation and clients.&lt;/p&gt;
&lt;h3 id=&#34;services&#34;&gt;Services&lt;/h3&gt;
&lt;p&gt;The tl;dr: of this section is that we are relying on the fact that OpenAPI 3.1 is a superset of JSON Schema Draft
2020-12 and are adding JSON Schema schemas to as many services as possible. When put this way, it sounds pretty simple
but as is often the case, the devil is in the detail. OpenStack is made up of multiple independent services maintained
by different groups of people, and while the Oslo project has helped ensure some level of consistency around things like
configuration (&lt;code&gt;oslo.conf&lt;/code&gt;), database connectivity (&lt;code&gt;oslo.db&lt;/code&gt;) and messaging (&lt;code&gt;oslo.messaging&lt;/code&gt;), there is huge variance
in the API frameworks used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Nova, Cinder, Glance, and Manila use a combination of &lt;a href=&#34;https://pypi.org/project/Routes/&#34;&gt;Routes&lt;/a&gt; (for routing), &lt;a href=&#34;https://pypi.org/project/WebOb/&#34;&gt;WebOb&lt;/a&gt; (for
request/response models), &lt;a href=&#34;https://pypi.org/project/Paste/&#34;&gt;Paste&lt;/a&gt; (for application dispatch) and &lt;a href=&#34;https://pypi.org/project/PasteDeploy/&#34;&gt;PasteDeploy&lt;/a&gt; (for
middleware).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Neutron uses the same Routes + WebOb + Paste + PasteDeploy combo used by Nova, Cinder, Glance, and Manila, but with
the addition of &lt;a href=&#34;https://pypi.org/project/pecan/&#34;&gt;Pecan&lt;/a&gt; in place of the &amp;ldquo;homegrown&amp;rdquo; WSGI frameworks used in those projects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Keystone uses &lt;a href=&#34;https://pypi.org/project/Flask/&#34;&gt;Flask&lt;/a&gt; and &lt;a href=&#34;https://pypi.org/project/Flask-RESTful/&#34;&gt;Flask-Restful&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Swift uses PasteDeploy (but I admittedly know very little about Swift).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ironic uses Pecan (which in turn uses WebOb, so you&amp;rsquo;ll see references to this too)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;etc etc&amp;hellip;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fortunately, despite the wide differences in frameworks, they pretty much all have the same building blocks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Controllers (or Applications) which are responsible for a given resource and handles calls for same. These are
typically classes with methods for each HTTP verb that the API supports.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Middleware that inspects and/or modifies requests and responses.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Routers that map a HTTP request to a controller. This is typically implemented as a special type of middleware.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this model, both parsing of requests and generation of responses happens in the controller methods, which makes this
a natural place to add validation for same. And there are a few things we want to validate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;API version (for services that uses &lt;a href=&#34;https://that.guru/blog/api-versioning-in-openstack/&#34;&gt;API microversions&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Request path and query string parameters&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Request and response bodies&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By wrapping the controller methods with decorators, we we are able to inspect both the request or response objects,
comparing them against schemas for same.&lt;/p&gt;
&lt;h3 id=&#34;tooling&#34;&gt;Tooling&lt;/h3&gt;
&lt;p&gt;Having schemas in place for all services is of little help if we don&amp;rsquo;t do anything with them. To this end, we want
tooling that can inspect services and extract their JSON Schema schemas, combining them to produce OpenAPI
schemas.&lt;/p&gt;
&lt;p&gt;Once we have these OpenAPI schemas, we can start generating (self-validating) documentation and clients/libraries. For
the former, OpenAPI will replace the &lt;code&gt;os-api-ref&lt;/code&gt; Sphinx extension currently used across OpenStack. &lt;code&gt;os-api-ref&lt;/code&gt; allows
us to describe our APIs in reStructuredText and it is a tool that has worked relatively well for us, but the lack of
machine readability means it&amp;rsquo;s hard to validate against the code. For the latter, we hope to lessen the burden of
maintaining libraries and clients in multiple languages, as this has proven very challenging to do given the very large
number of OpenStack APIs in multiple. Once again, OpenAPI is better suited to this challenge than &lt;code&gt;os-api-ref&lt;/code&gt; or
anything else we have.&lt;/p&gt;
&lt;h2 id=&#34;current-status-and-future-plans&#34;&gt;Current status and future plans&lt;/h2&gt;
&lt;h3 id=&#34;nova&#34;&gt;Nova&lt;/h3&gt;
&lt;p&gt;Nova started from the best position, since it was already using JSON Schema for request validation. However, there were
a couple of issues to overcome:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The version of JSON Schema used for the schemas, Draft 04, is over 12 years old, meaning the schemas needed migrating
to Draft 2020-12.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There were two different mechanisms for API versioning: if-else checks inside the controller methods, plus a decorator
that relied on the &lt;a href=&#34;https://docs.python.org/3/howto/descriptor.html&#34;&gt;descriptor protocol&lt;/a&gt; to allow us to define
version-specific controller methods. The latter made inspection of the API router more difficult than necessary and
had to be replaced.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;While many APIs had request query string and request body schemas, not all of them did.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Most importantly, there were no schemas for response bodies.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of these have now been resolved. We have bumped our schemas to Draft 2020-12, we have added all the missing request
schemas, and we have added response body schemas for a number of resources. The outstanding changes to address the API
versioning issues and add the remaining response body schemas are all ready and just waiting for review, so with any
luck we will be able to close this out in the 2025.2 (Flamingo) release. Once this work has merged, the final step will
be to start generating api-ref documentation from the OpenAPI schemas instead of using the &lt;code&gt;os-api-ref&lt;/code&gt; tool we
currently use. The patches for Nova can be found &lt;a href=&#34;https://review.opendev.org/q/project:openstack/nova+topic:openapi&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;cinder&#34;&gt;Cinder&lt;/h3&gt;
&lt;p&gt;Cinder was in a very similar position to Nova thanks to their shared lineage. This means it had the same advantage -
pre-existing use of JSON Schema for request validation - and the same issues. However, it also had the added issue of
having a mountain of tech debt mainly related to the support for multiple API versions that Cinder offered until
relatively recently. This has necessitated a lot of additional cleanup patches to do things like remove the
&lt;code&gt;cinder.api.v2&lt;/code&gt; module and consolidate everything under &lt;code&gt;cinder.api.v3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, while the bulk of the changes have been written, none of them have merged. I&amp;rsquo;m hoping we can double down
on this in 2025.2 (Flamingo) release and start making some progress. The patches for Cinder can be found
&lt;a href=&#34;https://review.opendev.org/q/project:openstack/nova+topic:openapi&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;keystone&#34;&gt;Keystone&lt;/h3&gt;
&lt;p&gt;Once again, Keystone had the good fortune of having existing use of JSON Schema for request query string and request
body validation, but this was done inconsistently and didn&amp;rsquo;t cover response body schemas. Most of the work here has
focused on adding these missing schemas and making the router easier to inspect by moving validation from inside the
controller methods to decorators and splitting methods that previously handling multiple endpoints (e.g. &lt;code&gt;GET /foo&lt;/code&gt; and
&lt;code&gt;GET /foo/123&lt;/code&gt;) into multiple single-purpose methods.&lt;/p&gt;
&lt;p&gt;Many resources have been fully specced by now but some remain. In addition, there are some decisions to be made
regarding support for undocumented API options that have been (re)discovered during this work. None of it should be
insurmountable, however. The patches for Keystone can be found
&lt;a href=&#34;https://review.opendev.org/q/project:openstack/keystone+topic:openapi&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;manila&#34;&gt;Manila&lt;/h3&gt;
&lt;p&gt;Yet another service with JSON Schema already in place. Once again, the work here consists of adding missing schemas and
making the router easier to inspect. A small number of patches have merged here and more are in-flight. The patches for
Manila can be found &lt;a href=&#34;https://review.opendev.org/q/project:openstack/manila+file:schemas&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;ironic&#34;&gt;Ironic&lt;/h3&gt;
&lt;p&gt;Ironic hasn&amp;rsquo;t historically used JSON Schema for validation, instead using its own homegrown validation framework and
taking advantage of some Pecan functionality to maintain API versioning. As a result, there&amp;rsquo;s quite a bit of work needed
to get schemas into Ironic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Move API versioning to decorators&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rework request path, query string and body parameter validation to use JSON Schema schemas (via decorators) instead of
the homegrown framework&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add request body parameter validation&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This work is in very early stages, but there are people working on it and there appears to be broad buy-in from the
Ironic team, so I hope to see significant progress over the course of the 2025.2 (Flamingo) cycle. The patches for
Ironic can be found &lt;a href=&#34;https://review.opendev.org/q/project:openstack/ironic+topic:openapi&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;other-services-neutron-swift-glance-octavia-&#34;&gt;Other services (Neutron, Swift, Glance, Octavia, &amp;hellip;)&lt;/h3&gt;
&lt;p&gt;To the best of my knowledge, no work has been started in other projects. Of the &amp;ldquo;core&amp;rdquo; projects, I expect the Glance
effort to be relatively small since they already have formal schemas (exposed via the API!) for most of their resources.
Conversely, I expect the Neutron effort to be both large and complicated, given the number and highly dynamic nature of
Neutron&amp;rsquo;s API, driven by it&amp;rsquo;s extension-based &amp;ldquo;versioning&amp;rdquo; system. I have somehow never worked on Swift and haven&amp;rsquo;t a
clue about that.&lt;/p&gt;
&lt;h3 id=&#34;tooling-1&#34;&gt;Tooling&lt;/h3&gt;
&lt;p&gt;There are two tooling-related efforts ongoing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Development of the &lt;a href=&#34;https://opendev.org/openstack/codegenerator&#34;&gt;codegenerator&lt;/a&gt; project.&lt;/li&gt;
&lt;li&gt;Improvements to the openstacksdk project, python-openstackclient project, and related projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The codegenerator project provides a collection of utilities for generating OpenAPI schemas and a (Rust-based 🦀) client
from these schemas. Development on this tool first started in earnest during the 2024.1 (Caracal) cycle, but the tool has
continued to evolve over the course of the 2024.2 (Dalmatian) and 2025.1 (Epoxy) cycles. I expect to see more progress
as schemas continue to get added to the Nova project. If you&amp;rsquo;re interested in learning more about this work, I&amp;rsquo;d
encourage to review the following material from my upstream colleague, Artem Goncharov (gtema), who is driving much of
it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gtema.github.io/slides/openstack_openapi/openapi_reveal.html&#34;&gt;OpenStack OpenAPI specs: Building up OpenAPI specs for OpenStack APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gtema.github.io/slides/rust_cli/rust_cli_reveal.html&#34;&gt;OpenStack CLI rewritten in Rust&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The openstacksdk and python-openstackclient projects, meanwhile, are far more established. As a quick reminder, the
openstacksdk project provides the primary OpenStack client library, while the python-openstackclient project is the
primary OpenStack CLI. In recent releases, we&amp;rsquo;ve been undertaking work to prepare them for a future when they can both
be at least partially auto-generated. This has taken the form of the addition of typing and a more general &amp;ldquo;removal of
weirdness&amp;rdquo; goal.&lt;/p&gt;
&lt;p&gt;The addition of typing is probably easier to grok and easier to measure. To achieve this, we have been adding type hints
to openstacksdk and python-openstackclient, as well as dependencies of same. As of the 2025.1 (Epoxy) release, we have
full type coverage for keystoneauth (a dependency of openstacksdk) and for both cliff and osc-lib (dependencies of
python-openstackclient). We also have initial coverage of both openstacksdk and python-openstackclient themselves, with
more comprehensive coverage of some of the non-core aspects of the former. Completely typing openstacksdk in particular
though is more challenging due to the aforementioned weirdness, but we will continue to make progress on this over the
2025.2 (Flamingo) cycle.&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;removal of weirdness&amp;rdquo; goal is, as we noted, a little more general and cover a few related issues we&amp;rsquo;re facing. The
two biggest of these, however, is (a) the over-use of variadic arguments and keyword arguments (&lt;code&gt;*args&lt;/code&gt; and &lt;code&gt;**kwargs&lt;/code&gt;)
in the proxy and cloud layers in particular, and (b) the munging of request and response body parameters in the
&lt;code&gt;Resource&lt;/code&gt; class used throughout openstacksdk. The former is hopefully self-explanatory so let&amp;rsquo;s focus on the latter. By
way of a demonstration of the issue, consider the compute service&amp;rsquo;s &lt;code&gt;/servers&lt;/code&gt; API. This API expects you to pass
&lt;code&gt;networks&lt;/code&gt; and &lt;code&gt;block_device_mapping_v2&lt;/code&gt; fields in the request for &lt;code&gt;POST /servers&lt;/code&gt;, to configure networking and block
devices respectively. However, it returns this information - in a different form, no less - via the &lt;code&gt;addresses&lt;/code&gt; and
&lt;code&gt;os-extended-volumes:volumes_attached&lt;/code&gt; in response to &lt;code&gt;GET /servers/{serverID}&lt;/code&gt; and &lt;code&gt;GET /servers/detail&lt;/code&gt;. Because we
currently munge the request and response body fields, our &lt;code&gt;Server&lt;/code&gt; class therefore looks like this (in highly minimized
form):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Server&lt;/span&gt;(resource&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Resource):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addresses &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resource&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Body(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;addresses&amp;#39;&lt;/span&gt;, type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;dict)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    attached_volumes &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resource&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Body(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;os-extended-volumes:volumes_attached&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        aka&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;volumes&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;list,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        list_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume_attachment&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;VolumeAttachment,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        default&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    block_device_mapping &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resource&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Body(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;block_device_mapping_v2&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    networks &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; resource&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Body(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;networks&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But if you attempt to set either &lt;code&gt;addresses&lt;/code&gt; or &lt;code&gt;attached_volumes&lt;/code&gt; before a &lt;code&gt;Server.create&lt;/code&gt; call, you will get an error.
On the other hand, if you do a &lt;code&gt;Server.fetch&lt;/code&gt; or &lt;code&gt;Server.list&lt;/code&gt; call, the &lt;code&gt;block_device_mapping&lt;/code&gt; and &lt;code&gt;networks&lt;/code&gt;
attributes will always be set to &lt;code&gt;None&lt;/code&gt; since they don&amp;rsquo;t form part of the response body. This has necessitated a whole
load of special casing in python-openstackclient, and special casing is bad for things you wish to auto-generate. By
separating these, we make it easier to type and easier to auto-generate. While a number of PoCs have been produced, none
have been implemented yet. We expect work on this to continue during the 2025.2 (Flamingo) cycle.&lt;/p&gt;
&lt;h2 id=&#34;questions&#34;&gt;Questions?&lt;/h2&gt;
&lt;p&gt;Come discuss these topics in the OpenStack Virtual PTG, running from 07-11 April 2025. More information of the event and
sessions can be found at &lt;a href=&#34;https://ptg.opendev.org/&#34;&gt;ptg.opendev.org&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Block Storage in OpenStack</title>
      <link>https://that.guru/blog/block-devices-in-openstack/</link>
      <pubDate>Thu, 22 Aug 2024 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/block-devices-in-openstack/</guid>
      <description>&lt;p&gt;It&amp;rsquo;s an often overlooked fact that OpenStack has two wholly different mechanisms for provisioning block devices for
instances. While most people are aware of the Block Storage service, Cinder, not everyone is aware of or gives much
thought to the various types of block devices or &lt;em&gt;&amp;ldquo;ephemeral&amp;rdquo;&lt;/em&gt; storage that the Compute service, Nova, is able to
provide. This article serves to provide a high-level overview and comparison of these two different mechanisms,
including examples of how you can use them and some hopefully (interesting) asides.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This article focuses on exclusively on block devices, ignoring the other types of storage available in OpenStack such as
objects (provided by Swift) or shareable filesystems (provided by Manila). Covering those will have to be left for
another day.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;types-of-block-storage&#34;&gt;Types of block storage&lt;/h2&gt;
&lt;p&gt;Broadly speaking, OpenStack has two types of block storage: storage provisioned by Nova, and storage provisioned by
Cinder. Somewhat confusingly, these are often referred to as &lt;em&gt;ephemeral&lt;/em&gt; storage and &lt;em&gt;persistent&lt;/em&gt; storage, respectively.
The origin for these names is likely based on the fact that Nova-provisioned storage is associated with an individual
instance and has a lifecycle that is tied to the lifecycle instance itself, meaning if you delete the instance then any
Nova-provisioned storage associated with that instance is also deleted. By comparison, Cinder-provisioned storage can
&amp;ldquo;persist&amp;rdquo; after an instance is deleted and may be re-attached to other instances (or in some cases even attached to
multiple instances at the same time).&lt;/p&gt;
&lt;p&gt;When and where you can use either type of storage depends on the type of disk you want to create, something we&amp;rsquo;ll get
to in a moment. However, to call these storage types ephemeral and persistent invites confusion on multiple fronts.
Firstly, there is the fact that Nova already has its own separate concept of &amp;ldquo;ephemeral disks&amp;rdquo;, which we cover below.
More pressingly, calling Nova-provisioned storage ephemeral suggests it is somehow unsafe or unreliable. In reality,
both Nova and Cinder provide a level of configurability about where or how the underlying data for the block devices are
stored. Cinder achieves this by being pluggable and supporting a wide variety of drivers, which can be found
&lt;a href=&#34;https://docs.openstack.org/cinder/latest/reference/support-matrix.html#driver-support-matrix&#34;&gt;here&lt;/a&gt;. Cinder supports backends like LVM and Ceph, as well as a large variety of other proprietary and
open source backends. For Nova, this is determined by the virt driver in use, the value of the &lt;code&gt;[compute] use_cow_images&lt;/code&gt; configuration option, and optionally one or more virt driver-specific configuration options. If using
the libvirt driver then the following options are all relevant:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[compute] use_cow_image&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[compute] force_raw_images&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[libvirt] images_types&lt;/code&gt; (and mechanism-specific options),&lt;/li&gt;
&lt;li&gt;and &lt;code&gt;[DEFAULT] instances_path&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In a default DevStack deployment, block devices will be stored as &lt;code&gt;qcow2&lt;/code&gt; image files in the path indicated by
&lt;code&gt;[DEFAULT] instances_path&lt;/code&gt;, &lt;code&gt;/opt/stack/data/nova/instances&lt;/code&gt;, on the compute host that instance is located on.
When using this default configuration, a migrated instance will need its block storage files migrated too and if the
host dies then you will lose any of these Nova-provisioned block devices with it. However, this is only one potential
configuration: you can also choose to store the images in Ceph (&lt;code&gt;[libvirt] images_types = rdb&lt;/code&gt;, with &lt;code&gt;[libvirt] images_rbd_pool&lt;/code&gt; and &lt;code&gt;[libvirt] images_rbd_ceph_conf&lt;/code&gt; set to relevant options). Alternatively, you can choose to use use
any of the other local storage mechanisms with a suitable replication or backup strategy, whether that&amp;rsquo;s LVM (&lt;code&gt;[libvirt] images_types = lvm&lt;/code&gt; and &lt;code&gt;[libvirt] images_volume_group&lt;/code&gt; set to a relevant volume group (VG)) and backups, or one of the
file-based mechanisms with the directory indicated by &lt;code&gt;[DEFAULT] instances_path&lt;/code&gt; placed on a network-attached
filesystem. Suffice to say that, with correct configuration, your data should never be at risk regardless of where it&amp;rsquo;s
placed.&lt;/p&gt;
&lt;h2 id=&#34;types-of-disk&#34;&gt;Types of disk&lt;/h2&gt;
&lt;p&gt;Now that we&amp;rsquo;re aware of &lt;em&gt;how&lt;/em&gt; the block storage is actually stored, let&amp;rsquo;s look at how these block devices are exposed
and used in OpenStack. OpenStack typically refers to block devices as either disks or volumes. I don&amp;rsquo;t have a good
explanation for when you&amp;rsquo;d use one or the other, and my own mental shortcut is to call any block device attached to an
instance a disk, unless that block device is a non-root device provisioned by Cinder in which case it&amp;rsquo;s a volume. With
that clarified (🙃), let&amp;rsquo;s take a look at the first disk type, root disks.&lt;/p&gt;
&lt;h3 id=&#34;root-disks&#34;&gt;Root disks&lt;/h3&gt;
&lt;p&gt;This is the one most people will be familiar with and it&amp;rsquo;s the only disk that&amp;rsquo;s absolutely required. As the name
suggests, the root disk is where &lt;code&gt;/&lt;/code&gt; is mounted. By default, the root disk will be provisioned by Nova and its size will
be configured in GB using the &lt;code&gt;disk&lt;/code&gt; property of the flavor used to create the instance. For example, by looking at the
&lt;code&gt;m1.small&lt;/code&gt; flavor on a local DevStack deployment I can see &lt;code&gt;disk&lt;/code&gt; set to &lt;code&gt;20&lt;/code&gt; or 20GB:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack flavor show m1.small -f value -c disk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we create a instance using this flavor (and no other block device-related configuration, obviously), we will get a
root disk that is 20GB in size.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can confirm this by SSH&amp;rsquo;ing into the machine:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server add floating ip test-server &lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;FIP&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server ssh test-server -- -l cirros
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ lsblk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vda     252:0    &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;  20G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|-vda1  252:1    &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;  20G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; part /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;-vda15 252:15   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   8M  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; part
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can also opt to use a Cinder volume for the root disk (which some people will now call a &amp;ldquo;root volume&amp;rdquo; - see previous
&amp;ldquo;clarification&amp;rdquo; 😅). If you&amp;rsquo;re using OpenStackClient (OSC), this can be accomplished using the &lt;code&gt;--boot-from-volume&lt;/code&gt;
option:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --boot-from-volume &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When you do this, Nova will create the volume for you by proxying the request through to Cinder. You can confirm this
using the &lt;code&gt;openstack volume list&lt;/code&gt; command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ openstack volume list
+--------------------------------------+------+--------+------+--------------------------------------+
| ID                                   | Name | Status | Size | Attached to                          |
+--------------------------------------+------+--------+------+--------------------------------------+
| 68584677-0d53-4c52-8d1c-5600c96768a1 |      | in-use |    5 | Attached to test-server on /dev/vda  |
+--------------------------------------+------+--------+------+--------------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And, like above, you can confirm the disk size in the guest:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda     252:0    0   5G  0 disk
|-vda1  252:1    0   5G  0 part /
`-vda15 252:15   0   8M  0 part
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, you can choose to use an existing volume as the root disk. To do this, use the &lt;code&gt;--volume&lt;/code&gt; option:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --volume test-volume &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This volume must be bootable, and you would likely use this to re-create a deleted instance using its root volume.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;It&amp;rsquo;s probably useful to note that, by default, volumes attached to an instance are not automatically removed when the
instance is deleted. This is either or a pro or a con, depending on how you look at it. On the positive side, this
allows you to re-attach the disk to a new instance (more on this later). On the negative side, this means you can end up
with a whole load of stale volumes sitting around, taking up space on your Ceph cluster (or whatever backend Cinder is
using). To work around this, you can modify the &lt;code&gt;delete_on_termination&lt;/code&gt; attribute of the volume attachment:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server volume list test-server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------+--------------------------------------+--------------------------------------+------+------------------------+--------------------------------------+--------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Device   | Server ID                            | Volume ID                            | Tag  | Delete On Termination? | Attachment ID                        | BlockDeviceMapping UUID              |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------+--------------------------------------+--------------------------------------+------+------------------------+--------------------------------------+--------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| /dev/vda | 339acd3a-41e3-45b9-8dbf-1d7dcc1bd8bc | 1bd2c131-7961-4eeb-84ff-4990917bb9b8 | None | False                  | 819c7472-b14f-4867-b894-b68758c6d398 | c69ba987-e66a-4381-8c8a-ceff5f652f0e |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------+--------------------------------------+--------------------------------------+------+------------------------+--------------------------------------+--------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server volume set --delete-on-termination 339acd3a-41e3-45b9-8dbf-1d7dcc1bd8bc 1bd2c131-7961-4eeb-84ff-4990917bb9b8
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alternatively, you can set this property when creating the instance. Unfortunately doing this requires using the
&lt;code&gt;--block-device&lt;/code&gt; option to specify the BDM rather than the &lt;code&gt;--boot-from-volume&lt;/code&gt; alias. The equivalent of the
previous command using &lt;code&gt;--boot-from-volume&lt;/code&gt; would be:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ image_id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;openstack image show cirros-0.6.2-x86_64-disk -f value -c id&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;image_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;,source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;image,destination_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume,delete_on_termination&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;true,boot_index&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;0,volume_size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/aside&gt;

&lt;h3 id=&#34;ephemeral-disks&#34;&gt;Ephemeral disks&lt;/h3&gt;
&lt;p&gt;Ephemeral disks are so called because they are associated with a single instance and only exist for the lifetime of that
instance. Therefore, as you may guess, these disks can only be provided by Nova. Like root disks, the size of the
ephemeral disk is configured in GB via a flavor property, &lt;code&gt;OS-FLV-EXT-DATA:ephemeral&lt;/code&gt;. None of the flavors provided in a
default DevStack install provide for ephemeral storage, so to demonstrate this we need to create our own flavor which
we&amp;rsquo;re going to call &lt;code&gt;m1.ephemeral&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack flavor create --id &lt;span style=&#34;color:#ae81ff&#34;&gt;99&lt;/span&gt; --vcpus &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; --ram &lt;span style=&#34;color:#ae81ff&#34;&gt;512&lt;/span&gt; --disk &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt; --ephemeral &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; m1.ephemeral
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once created, we can look at the flavor and ensure that the &lt;code&gt;OS-FLV-EXT-DATA:ephemeral&lt;/code&gt; property has been set to the
relevant size (in this case, &lt;code&gt;10&lt;/code&gt; or 10GB):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack flavor show m1.ephemeral -f value -c &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;OS-FLV-EXT-DATA:ephemeral&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we create a instance using this flavor, we will get a root disk that is 20GB in size &lt;strong&gt;and&lt;/strong&gt; a new, additional disk
that is 10GB in size:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.ephemeral --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once again, we can confirm this by SSH&amp;rsquo;ing into the machine:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ lsblk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vda     252:0    &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   5G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|-vda1  252:1    &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   5G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; part /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;-vda15 252:15   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   8M  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; part
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vdb     252:16   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;  10G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; disk /mnt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll note that the disk is already mounted for us. This isn&amp;rsquo;t actually an OpenStack doing this for us. Rather, it&amp;rsquo;s
part of &lt;code&gt;cloud-init&lt;/code&gt;, which is included in the Cirros images. If you were using an image that didn&amp;rsquo;t include
&lt;code&gt;cloud-init&lt;/code&gt; or were to attach multiple ephemeral disks, you&amp;rsquo;d need to handle this mounting yourself.&lt;/p&gt;
&lt;p&gt;You may also note that there&amp;rsquo;s an &lt;code&gt;--ephemeral&lt;/code&gt; option available for the &lt;code&gt;openstack server create&lt;/code&gt; command. This option
allows you to change both the layout of the ephemeral storage and the filesystem used on them. For example, say that
instead of having a single 10GB disk, I wanted to have an 8GB disk formatted as ext4 and a 2GB disk formatted as XFS, I
could invoke OSC like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.ephemeral --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --ephemeral size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;8,format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;ext4 --ephemeral size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;2,format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;xfs &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can see that these are available in the machine using &lt;code&gt;lsblk&lt;/code&gt; again and confirm their filesystem types using &lt;code&gt;blkid&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ lsblk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vda     252:0    &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;  20G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|-vda1  252:1    &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;  20G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; part /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;-vda15 252:15   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   8M  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; part
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vdb     252:16   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   8G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; disk /mnt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vdc     252:32   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   2G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ blkid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/dev/vdb: LABEL&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ephemeral0&amp;#34;&lt;/span&gt; UUID&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;a34015c1-be25-4757-8d17-cc82285b12b2&amp;#34;&lt;/span&gt; BLOCK_SIZE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4096&amp;#34;&lt;/span&gt; TYPE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ext4&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/dev/vdc: LABEL&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ephemeral1&amp;#34;&lt;/span&gt; UUID&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0cf5d85b-0357-44fc-9549-6f71720af8ba&amp;#34;&lt;/span&gt; BLOCK_SIZE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;512&amp;#34;&lt;/span&gt; TYPE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xfs&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/dev/vda15: SEC_TYPE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;msdos&amp;#34;&lt;/span&gt; UUID&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AE31-5342&amp;#34;&lt;/span&gt; BLOCK_SIZE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;512&amp;#34;&lt;/span&gt; TYPE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;vfat&amp;#34;&lt;/span&gt; PARTUUID&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;05ecb6a9-6cb3-4697-b819-25c6cee5bd9a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/dev/vda1: LABEL&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cirros-rootfs&amp;#34;&lt;/span&gt; UUID&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;f1511162-06fb-4482-9dab-9a0c76633fb2&amp;#34;&lt;/span&gt; BLOCK_SIZE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4096&amp;#34;&lt;/span&gt; TYPE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ext3&amp;#34;&lt;/span&gt; PARTUUID&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;df2f017d-edcc-4371-81c7-fcfa0c5c1b09&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When configuring ephemeral disks this way, the total size of all disks does not have to add up to the size indicted by
the flavor&amp;rsquo;s &lt;code&gt;OS-FLV-EXT-DATA:ephemeral&lt;/code&gt; property (meaning you could choose only to configure e.g. the 2GB volume above)
but it cannot exceed the size indicated in the flavor. For example, if I repeated the above command but using single
20GB disk (i.e. 10GB larger than the 10GB value specified for the &lt;code&gt;m1.ephemeral&lt;/code&gt; flavor we created previously), the
request will fail:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.ephemeral --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --ephemeral size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BadRequestException: 400: Client Error &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url: http://10.0.108.50/compute/v2.1/servers, Ephemeral disks requested are
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;larger than the instance type allows. If no size is given in one block device mapping, flavor ephemeral size will be
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;used.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, as we noted at the top, ephemeral disks can only be provisioned by Nova. If you attempt to create an ephemeral
disk using &lt;code&gt;destination_type=remote&lt;/code&gt;, what you have is no longer an ephemeral disk (in the Nova sense of the term)
but rather a volume. Which is a nice segue to&amp;hellip;&lt;/p&gt;
&lt;h3 id=&#34;volumes&#34;&gt;Volumes&lt;/h3&gt;
&lt;p&gt;Volumes are not a disk type but rather the name given to the primary resource type provided by Cinder. As noted
previously, a Cinder volume can be used as the backing device for the root disk, but it can also be used to provide
additional disks to the instance (but not ephemeral disks - an ephemeral disk ceases to be an ephemeral disk once it&amp;rsquo;s
no longer provisioned by Nova). For example, say we wanted to use a standard Nova-provisioned block device for our
root disk but attach two additional volumes provisioned by Cinder, we could run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ vol_a_id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;openstack volume create --size &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; test-volume-a -f value -c id&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ vol_b_id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;openstack volume create --size &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; test-volume-b -f value -c id&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;vol_a_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;,source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;vol_b_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;,source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can examine these in the guest:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ lsblk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vda     252:0    &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;  20G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|-vda1  252:1    &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;  20G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; part /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;-vda15 252:15   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   8M  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; part
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vdb     252:16   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   5G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vdc     252:32   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;   5G  &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; disk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead of pre-creating the volume with Cinder, we could also let Nova do this for us. This avoids an extra call to
Cinder (at least from us - Nova simply makes the call on our behalf), at the expense of some fine grained configuration
for the volume. We could modify the above command like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;blank,destination_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume,volume_size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;blank,destination_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume,volume_size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unlike the block devices provisioned by Nova, it&amp;rsquo;s also possible to attach additional block devices to an existing
instance:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack volume create --size &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; test-volume-c
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server add volume test-server test-volume-c
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Likewise, you can remove devices as long as they&amp;rsquo;re not used for the root disk:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server remove volume test-server test-volume-c
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, Cinder volumes can be attached to multiple instances at the same time. As discussed in the &lt;a href=&#34;https://docs.openstack.org/cinder/latest/admin/volume-multiattach.html&#34;&gt;Cinder
documentation&lt;/a&gt;, this requires the use of a clustered or multi-attach creation of a special &lt;em&gt;volume type&lt;/em&gt;
with the &lt;code&gt;multiattach&lt;/code&gt; property set to &lt;code&gt;&amp;lt;is&amp;gt; True&lt;/code&gt;. Fortunately, the reference LVM driver configured by default in a
DevStack deployment supports this, so we can demo creating the volume type, volume, and instances:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack volume type create --multiattach multiattach
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ vol_id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;openstack volume create --size &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; --type multiattach test-volume -f value -c id&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;vol_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;,source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server-a
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;vol_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;,source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server-b
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we log in to these two instances and mount the volume (after partitioning and formatting it), we&amp;rsquo;ll be able to create
a file in one and see it appear in the other:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ openstack server ssh test-server-a -- -l cirros

# create partitions using fdisk since parted is not available in cirros images
$ sudo fdisk /dev/vdb
$ sudo mkfs.ext4 /dev/vdb1

$ lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda     252:0    0  20G  0 disk
|-vda1  252:1    0  20G  0 part /
`-vda15 252:15   0   8M  0 part
vdb     252:16   0   5G  0 disk
`-vdb1  252:17   0   5G  0 part
$ mkdir test
$ sudo mount /dev/vdb1 test
$ sudo touch test/foo
$ exit

❯ openstack server ssh test-server-b -- -l cirros

$ mkdir test
$ sudo mount /dev/vdb1 test  # you may need to reboot to pick up the changes to this disk for this to work
$ ls test
$ ls disk/
foo
&lt;/code&gt;&lt;/pre&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;If &lt;code&gt;parted&lt;/code&gt; was available in the Cirros image used, we could avoid the interactive use of &lt;code&gt;fdisk&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo parted /dev/vdb mklabel gpt
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo parted /dev/vdb mkpart primary ext4 0% 100%
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo parted /dev/vdc print
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/aside&gt;

&lt;h3 id=&#34;swap-disks&#34;&gt;Swap disks&lt;/h3&gt;
&lt;p&gt;The final type of disk is the swap disk. Swap disks are very like ephemeral disks, in that they&amp;rsquo;re exclusively managed
by Nova and configured via a flavor property called &lt;code&gt;swap&lt;/code&gt;. Unlike the &lt;code&gt;disk&lt;/code&gt; and &lt;code&gt;OS-FLV-EXT-DATA:ephemeral&lt;/code&gt; properties
though, the &lt;code&gt;swap&lt;/code&gt; property is a size in MB, not GB. Once again, the default DevStack configuration does not include
flavors with swap enabled so we need to configure these ourselves. Lets do that, creating a flavor called &lt;code&gt;m1.swap&lt;/code&gt; with
2048 MB of swap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack flavor create --id &lt;span style=&#34;color:#ae81ff&#34;&gt;98&lt;/span&gt; --vcpus &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; --ram &lt;span style=&#34;color:#ae81ff&#34;&gt;512&lt;/span&gt; --disk &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt; --swap &lt;span style=&#34;color:#ae81ff&#34;&gt;2048&lt;/span&gt; m1.swap
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once created, we can look at the flavor and ensure that the &lt;code&gt;swap&lt;/code&gt; property has been set to the relevant size (in this
case, &lt;code&gt;1024&lt;/code&gt; or 1024MB):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack flavor show m1.swap -f value -c &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;swap&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1024&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we create a instance using this flavor, we will get a root disk that is 20GB in size &lt;strong&gt;and&lt;/strong&gt; a additional disk
that is 1024MB in size and formatted as a swap disk:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.swap --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Inspecting the guest itself, we see the disk present and that it has an fstype of &lt;code&gt;swap&lt;/code&gt;, as expected:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo lsblk -f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME    FSTYPE FSVER LABEL         UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vda
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|-vda1  ext3         cirros-rootfs f1511162-06fb-4482-9dab-9a0c76633fb2   18.4G     0% /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;-vda15 vfat                       AE31-5342
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vdb     swap                       6f7adada-a19f-47e1-915c-20570535a619
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cat /proc/meminfo | grep -i swap
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SwapCached:            &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; kB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SwapTotal:             &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; kB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SwapFree:              &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; kB
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, unlike ephemeral disks, &lt;code&gt;cloud-init&lt;/code&gt; does not mount this additional disk for us. If you want that, you&amp;rsquo;ll need
to do so manually:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo swapon /dev/vdb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo lsblk -f
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME    FSTYPE FSVER LABEL         UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vda
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|-vda1  ext3         cirros-rootfs f1511162-06fb-4482-9dab-9a0c76633fb2   18.4G     0% /
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;-vda15 vfat                       AE31-5342
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vdb     swap                       6f7adada-a19f-47e1-915c-20570535a619
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cat /proc/meminfo | grep -i swap
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SwapCached:            &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; kB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SwapTotal:       &lt;span style=&#34;color:#ae81ff&#34;&gt;1048572&lt;/span&gt; kB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SwapFree:        &lt;span style=&#34;color:#ae81ff&#34;&gt;1048572&lt;/span&gt; kB
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Like ephemeral disk, there&amp;rsquo;s also a &lt;code&gt;--swap&lt;/code&gt; option for the &lt;code&gt;openstack server create&lt;/code&gt; command that you can use to
override the default swap size. Once again you cannot exceed the total size given in the flavor, but you also can&amp;rsquo;t
specify the option multiple times to divide the swap into different disks. For example, if we wanted to configure a
smaller swap disk size of, say, 512MB, we could do:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.swap --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --swap &lt;span style=&#34;color:#ae81ff&#34;&gt;512&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Attempting to use something larger than the size indicated in the &lt;code&gt;swap&lt;/code&gt; property of the flavor will result in an error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.swap --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --swap &lt;span style=&#34;color:#ae81ff&#34;&gt;2048&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BadRequestException: 400: Client Error &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url: http://10.0.108.50/compute/v2.1/servers, Swap drive requested is larger
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;than instance type allows.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;block-device-mappings-bdms&#34;&gt;Block Device Mappings (BDMs)&lt;/h2&gt;
&lt;p&gt;Finally, we get to the final piece of the block storage puzzle: Block Device Mappings, or BDMs. BDMs are how Nova
describes mapping of block devices to instances. BDMs are specified during instance creation using the
&lt;code&gt;block_device_mapping_v2&lt;/code&gt; field and are objects with a number of well known fields.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;The term &amp;ldquo;BDM&amp;rdquo; can also be used to refer to the (Python) objects used internally in Nova to manage mappings. If you&amp;rsquo;re
interested in a deeper dive into this particular topic, my colleague Lee Yarwood wrote a good overview a few years ago
which you can find &lt;a href=&#34;https://blog.yarwood.me.uk/2021/01/20/openstack_nova_bdm/&#34;&gt;here&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;Both the &lt;a href=&#34;https://docs.openstack.org/nova/latest/user/block-device-mapping.html&#34;&gt;Nova user docs&lt;/a&gt; and the &lt;a href=&#34;https://docs.openstack.org/api-ref/compute/?expanded=create-server-detail#create-server&#34;&gt;Nova API Reference docs&lt;/a&gt; provide a good overview of these
fields, but there are a few in particular worth discussing here in light of the above.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;destination_type&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This field, which has been referenced earlier in this article, determines what service manages the block devices and
therefore where the block device resides. This value can either be &lt;code&gt;local&lt;/code&gt; (meaning managed by Nova) or &lt;code&gt;remote&lt;/code&gt;
(managed by Cinder).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;source_type&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This indicates the source of the block devices. This value can one of &lt;code&gt;blank&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;snapshot&lt;/code&gt;, or &lt;code&gt;volume&lt;/code&gt;, and
the docs describe all of these in detail. We&amp;rsquo;ll explore some different use cases for these shortly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;boot_index&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is an integer value that indicates the order that the VM will use when attempting to boot from disks. When
attaching a volume as a root disk (i.e. boot from volume), you will use a &lt;code&gt;boot_index&lt;/code&gt; of &lt;code&gt;0&lt;/code&gt; to indicate that the
guest OS should boot from that volume from.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that knowledge in hand, we can look at a few of the examples previously from the perspective of the BDMs used
during instance creation. We&amp;rsquo;ll do this using the &lt;code&gt;openstack server create&lt;/code&gt; command with the &lt;code&gt;--debug&lt;/code&gt; flag, which
allows us to see the raw requests and responses issued to the server.&lt;/p&gt;
&lt;p&gt;First, let&amp;rsquo;s look a root disks. If we create a &amp;ldquo;standard&amp;rdquo; instance using a local root disk, then the
&lt;code&gt;block_device_mapping_v2&lt;/code&gt; field can be (and, if using OSC, will be) empty:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --debug &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;server&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;flavorRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;imageRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is because Nova automatically creates the BDM for us, making it unnecessary to specify. If we want to use boot from
volume instead, placing our root disk on a volume, we will need to specify a BDM:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --boot-from-volume &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; --debug &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;server&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;flavorRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;block_device_mapping_v2&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;uuid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;boot_index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;volume&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;volume_size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;imageRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(&lt;code&gt;9acb21b3-0516-459a-9b0a-6357e66ff74a&lt;/code&gt; is the ID of the &lt;code&gt;cirros-0.6.2-x86_64-disk&lt;/code&gt; image we created the server with,
and it&amp;rsquo;s passed via the &lt;code&gt;uuid&lt;/code&gt; field of the BDM rather than via the &lt;code&gt;imageRef&lt;/code&gt; field)&lt;/p&gt;
&lt;p&gt;Next, let&amp;rsquo;s look at ephemeral disks. As with root disks, you can simply use a relevant flavor and omit the
&lt;code&gt;block_device_mapping_v2&lt;/code&gt; field and Nova will automatically do the right thing. If you wanted to divide the ephemeral
disk into multiple devices though, you&amp;rsquo;d use a different variation of this field. For example, our prior example of an
8GB ext4 ephemeral disk and a 2GB XFS disk would look like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.ephemeral --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --ephemeral size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;8,format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;ext4 --ephemeral size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;2,format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;xfs &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;server&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;flavorRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;99&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;block_device_mapping_v2&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;uuid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;boot_index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;local&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;delete_on_termination&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;boot_index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;-1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;blank&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;local&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;delete_on_termination&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;volume_size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;guest_format&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ext4&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;boot_index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;-1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;blank&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;local&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;delete_on_termination&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;volume_size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;guest_format&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;xfs&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;imageRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yes, things are starting to get lengthy&amp;hellip;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll skip over volumes for a moment and move onto swap volumes. Yet again, if you don&amp;rsquo;t want to do anything bar use the
swap configured in the flavor then you can omit the &lt;code&gt;block_device_mapping_v2&lt;/code&gt; field, but you&amp;rsquo;ll need to specify it if
you wish to use a different size:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.swap --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --swap &lt;span style=&#34;color:#ae81ff&#34;&gt;512&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;server&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;flavorRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;98&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;block_device_mapping_v2&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;uuid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;boot_index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;local&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;delete_on_termination&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;boot_index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;-1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;blank&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;local&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;guest_format&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;swap&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;volume_size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;512&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;delete_on_termination&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;imageRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally there are volumes. As discussed above, there are huge variety of combinations available here and we&amp;rsquo;re only
going to look at a few of those we previously discussed. Firstly, let&amp;rsquo;s look at our example of using two precreated
volumes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ vol_a_id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;openstack volume create --size &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; test-volume-a -f value -c id&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ vol_b_id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;openstack volume create --size &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; test-volume-b -f value -c id&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;vol_a_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;,source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;vol_b_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;,source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;server&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;flavorRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;block_device_mapping_v2&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;uuid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;boot_index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;local&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;delete_on_termination&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;uuid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;668d9851-b878-4fe5-a244-67585c963a09&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;volume&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;volume&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;uuid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ed7c9976-9fe6-4ada-93cb-4e60bc2a0a05&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;volume&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;volume&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;imageRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And now compare this to our example of letting Nova create the volumes for us:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --flavor m1.small --image cirros-0.6.2-x86_64-disk --network private --key-name test-key &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;blank,destination_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume,volume_size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --block-device source_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;blank,destination_type&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;volume,volume_size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    test-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;server&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;flavorRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;block_device_mapping_v2&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;uuid&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;boot_index&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;image&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;local&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;delete_on_termination&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;blank&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;volume&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;volume_size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;source_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;blank&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;destination_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;volume&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;volume_size&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;imageRef&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;9acb21b3-0516-459a-9b0a-6357e66ff74a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are loads of other combinations available here, from creating volumes from snapshots to tagging created volumes,
but this should be sufficient to demonstrate the general &amp;ldquo;feel&amp;rdquo; of BDMs.&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;The main takeaway from this article should be that if you want to understand block devices in OpenStack, you&amp;rsquo;d be well
placed to understand BDMs. While they are terse, they do expose the most important aspects of block devices in
OpenStack, such as their differing sources and variety of ways that they can be configured. Does that mean that this
article is written backwards and should open with a piece on BDMs? Probably, but what fun would that be.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Availability Zones in Openstack and Openshift (Part 2)</title>
      <link>https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-2/</link>
      <pubDate>Fri, 03 May 2024 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-2/</guid>
      <description>
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This is part two of two. If you&amp;rsquo;re looking for part one, you can find it
&lt;a href=&#34;https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-1&#34;&gt;here&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;After seeing a few too many availability zone-related issues popping up in OpenShift clusters of late, I&amp;rsquo;ve decided it
might make sense to document the situation with OpenStack AZs on OpenShift (and, by extension, Kubernetes). This is the
second part of two. &lt;a href=&#34;https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-1&#34;&gt;The first part&lt;/a&gt; provided some background on what AZs are and how you can configure them,
while this part will examine how AZs affect OpenShift and Kubernetes components such as the OpenStack Machine API
Provider, the OpenStack Cluster API Provider, and the Cinder and Manila CSI drivers.&lt;/p&gt;
&lt;h2 id=&#34;the-line-up&#34;&gt;The line up&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a couple of OpenStack-specific components we need to be aware of in a typical OpenShift-on-OpenStack (a.k.a.
ShiftStack) deployment. My former colleague Michał Dulko &lt;a href=&#34;https://dulek.github.io/2022/07/14/capo-mapo-cloud-provider.html&#34;&gt;provided a good overview of many of these on his
blog&lt;/a&gt; but to (re-)summarise, you&amp;rsquo;ve got:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cloud Provider OpenStack (CPO)&lt;/li&gt;
&lt;li&gt;Machine API Provider OpenStack (MAPO)&lt;/li&gt;
&lt;li&gt;Cluster API Provider OpenStack (CAPO)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to these three components, there are two others to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cinder CSI Driver&lt;/li&gt;
&lt;li&gt;Manila CSI Driver&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Today we&amp;rsquo;re going to take a look at three of these five components - CPO, MAPO, and the Cinder CSI Driver - and explore
how availability zones - both Compute and Block Storage - impact them.&lt;/p&gt;
&lt;h2 id=&#34;cloud-provider-openstack&#34;&gt;Cloud Provider OpenStack&lt;/h2&gt;
&lt;p&gt;In contrast to a favoured programming language of mine 🐍, Kubernetes operates on very much batteries &lt;em&gt;not&lt;/em&gt; included
model. It does not provide out-of-the-box support for such important things as block storage, networking, or ingress. To
resolve this, you normally run Kubernetes on top of another platform - be that AWS, vSphere, GCE, or in our case
OpenStack - and add additional components that provide integration between your Kubernetes cluster and said platform and
its APIs. The cloud-provider interface provides one part of the integration puzzle here, managing the lifecycle of
&lt;code&gt;Node&lt;/code&gt;s (including their removal from the cluster if the underlying instance is deleted), &lt;code&gt;Service&lt;/code&gt;s of type
&lt;code&gt;LoadBalancer&lt;/code&gt;, and routes.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;If you&amp;rsquo;re interested, &lt;a href=&#34;https://medium.com/@m.json/the-kubernetes-cloud-controller-manager-d440af0d2be5&#34;&gt;this blog from Mikael Johansson&lt;/a&gt; provides a far more thorough overview of this
component.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;The OpenStack CCM uses Compute AZ information for the underlying instance to label the corresponding &lt;code&gt;Node&lt;/code&gt;. It sets two
labels, the &lt;a href=&#34;https://kubernetes.io/docs/reference/labels-annotations-taints/#topologykubernetesiozone&#34;&gt;&lt;code&gt;topology.kubernetes.io/zone&lt;/code&gt;&lt;/a&gt; label and the legacy
&lt;a href=&#34;https://kubernetes.io/docs/reference/labels-annotations-taints/#failure-domainbetakubernetesiozone&#34;&gt;&lt;code&gt;failure-domain.beta.kubernetes.io/zone&lt;/code&gt;&lt;/a&gt; label. You can see this if you retrieve the labels
for a &lt;code&gt;Node&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get Node -o jsonpath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{.items[*].metadata.labels}&amp;#39;&lt;/span&gt; | jq
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;beta.kubernetes.io/arch&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;amd64&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;beta.kubernetes.io/instance-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ci.m1.xlarge&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;beta.kubernetes.io/os&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linux&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;failure-domain.beta.kubernetes.io/region&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;regionOne&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;failure-domain.beta.kubernetes.io/zone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nova&amp;#34;&lt;/span&gt;,  &lt;span style=&#34;color:#75715e&#34;&gt;# &amp;lt;--- !!! here !!!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubernetes.io/arch&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;amd64&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubernetes.io/hostname&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stephenfin-5ps6d-master-0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubernetes.io/os&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linux&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node-role.kubernetes.io/control-plane&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node-role.kubernetes.io/master&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node.kubernetes.io/instance-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ci.m1.xlarge&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node.openshift.io/os_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rhcos&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;topology.cinder.csi.openstack.org/zone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nova&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;topology.kubernetes.io/region&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;regionOne&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;topology.kubernetes.io/zone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nova&amp;#34;&lt;/span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# &amp;lt;--- !!! and here !!!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;It also sets two corresponding labels - the &lt;a href=&#34;https://kubernetes.io/docs/reference/labels-annotations-taints/#topologykubernetesioregion&#34;&gt;&lt;code&gt;topology.kubernetes.io/region&lt;/code&gt;&lt;/a&gt; label and the legacy
&lt;a href=&#34;https://kubernetes.io/docs/reference/labels-annotations-taints/#failure-domainbetakubernetesioregion&#34;&gt;&lt;code&gt;failure-domain.beta.kubernetes.io/region&lt;/code&gt;&lt;/a&gt; label - but these are sourced from
Keystone-related information.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;To fetch the AZ information, OpenStack CCM queries the Nova API. You can see that happening
&lt;a href=&#34;https://github.com/openshift/cloud-provider-openstack/blob/release-4.15/pkg/openstack/openstack.go#L411-L463&#34;&gt;here&lt;/a&gt;,
and publishes this information via the &lt;code&gt;GetZoneByProviderID&lt;/code&gt; and &lt;code&gt;GetZoneByName&lt;/code&gt; functions, which form part of the
&lt;a href=&#34;https://github.com/kubernetes/cloud-provider/blob/v0.30.0/cloud.go#L281-L289&#34;&gt;cloud-provider API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Because the labels are defined on &lt;code&gt;Node&lt;/code&gt;s, they are useful for controlling the scheduling of pods, allowing users to
spread pods across multiple AZs. This is discussed in more details in the Kubernetes docs, in &lt;a href=&#34;https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/&#34;&gt;Assigning Pods to
Nodes&lt;/a&gt; and &lt;a href=&#34;https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/&#34;&gt;Pod Topology Spread
Constraints&lt;/a&gt; for example. They&amp;rsquo;re
also used for other topology-related features, such as &lt;a href=&#34;https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/&#34;&gt;Topology Aware
Routing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With regards to configuring these labels, there&amp;rsquo;s currently nothing to stop you modifying the labels in place. You
shouldn&amp;rsquo;t do this, however, since it doesn&amp;rsquo;t change the AZ of the underlying instance and pretty much kills whatever
advantage the topology feature has. If you want to change the AZ of the &lt;code&gt;Node&lt;/code&gt; then you&amp;rsquo;ll need to either migrate it (an
operation that comes with its own issues) or recreate it.&lt;/p&gt;
&lt;h2 id=&#34;machine-api-provider-openstack&#34;&gt;Machine API Provider OpenStack&lt;/h2&gt;
&lt;p&gt;The Machine API Provider OpenStack, or MAPO, is a Machine API provider for the OpenStack platform. The Machine API
allows you to scale up or scale down your cluster based on workload policies or other preferences and functions quite
similarly to the Cluster API, albeit with a different API. You can create &lt;code&gt;Machine&lt;/code&gt;s manually, but its more common to
instead create or modify &lt;code&gt;MachineSet&lt;/code&gt;s (for workers) or &lt;code&gt;ControlPlaneMachineSet&lt;/code&gt;s (for masters). You can find more
information about the Machine API in the &lt;a href=&#34;https://docs.openshift.com/container-platform/4.15/machine_management/index.html&#34;&gt;OpenShift
documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Like OpenStack CCM, MAPO uses AZ information to label resources - this time setting the &lt;code&gt;machine.openshift.io/zone&lt;/code&gt;
label on &lt;code&gt;Machine&lt;/code&gt; resources:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get -A Machine -o jsonpath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{.items[*].metadata.labels}&amp;#39;&lt;/span&gt; | jq
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;machine.openshift.io/cluster-api-cluster&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stephenfin-5ps6d&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;machine.openshift.io/cluster-api-machine-role&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;master&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;machine.openshift.io/cluster-api-machine-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;master&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;machine.openshift.io/instance-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ci.m1.xlarge&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;machine.openshift.io/region&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;regionOne&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;machine.openshift.io/zone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nova&amp;#34;&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# &amp;lt;-- !!! here !!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;It also sets an additional related labels - the &lt;code&gt;machine.openshift.io/region&lt;/code&gt; label - but this is sourced from Keystone
information.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;Also like OpenStack CCM, MAPO sources this AZ information from the Nova API, as you can see
&lt;a href=&#34;https://github.com/openshift/machine-api-provider-openstack/blob/release-4.15/pkg/machine/actuator.go#L226&#34;&gt;here&lt;/a&gt;
(&lt;code&gt;instanceStatus&lt;/code&gt; is a thin wrapper around a &lt;code&gt;ServerExt&lt;/code&gt; resource used by Gophercloud). They&amp;rsquo;re also editable but again,
editing them won&amp;rsquo;t actually change the AZ on the underlying instance and you&amp;rsquo;ll need to make changes elsewhere to do
this. However, unlike with &lt;code&gt;Node&lt;/code&gt;s, you can configure the AZ of a new or existing &lt;code&gt;Machine&lt;/code&gt; or &lt;code&gt;MachineSet&lt;/code&gt; /
&lt;code&gt;ControlPlaneMachineSet&lt;/code&gt; as part of the object definition and &lt;em&gt;this&lt;/em&gt; change will get reflected in the labels, both of
the &lt;code&gt;Machine&lt;/code&gt; and of the &lt;code&gt;Node&lt;/code&gt;. For example, to define a AZ when creating a new &lt;code&gt;MachineSet&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;machine.openshift.io/v1beta1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;MachineSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;&amp;lt;infrastructure_id&amp;gt;-&amp;lt;role&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;openshift-machine-api&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;template&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;providerSpec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;value&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;availabilityZone&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;nova-az0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;rootVolume&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;availabilityZone&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cinder-az0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This is described in more details in the &lt;a href=&#34;https://docs.openshift.com/container-platform/4.15/machine_management/creating_machinesets/creating-machineset-osp.html&#34;&gt;OpenShift
documentation&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;Alternatively, to define one for a &lt;code&gt;ControlPlaneMachineSet&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;machine.openshift.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ControlPlaneMachineSet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cluster&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;openshift-machine-api&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;template&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;machines_v1beta1_machine_openshift_io&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;failureDomains&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;OpenStack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;openstack&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;availabilityZone&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;nova-az0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;rootVolume&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;availabilityZone&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cinder-az0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;availabilityZone&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;nova-az1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;rootVolume&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;availabilityZone&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cinder-az1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;availabilityZone&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;nova-az2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;rootVolume&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;availabilityZone&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cinder-az2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This is described in more detail in the &lt;a href=&#34;https://docs.openshift.com/container-platform/4.15/machine_management/control_plane_machine_management/cpmso-configuration.html#cpmso-sample-yaml-openstack_cpmso-configuration&#34;&gt;OpenShift
documentation&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;cinder-csi-driver&#34;&gt;Cinder CSI Driver&lt;/h2&gt;
&lt;p&gt;The last component we&amp;rsquo;re going to look at here is the Cinder CSI Driver. The Container Storage Interface (CSI) defines a
standardised way to expose arbitrary block and file storage systems to Kubernetes workloads, allowing us to plug in
storage backends for various cloud platforms or networked storage solutions like NFS or SMB. As you might suspect, the
Cinder CSI Driver allows us to plug in storage from the OpenStack Block Storage service, Cinder, and to create
&lt;code&gt;PersistentVolume&lt;/code&gt;s or &lt;code&gt;PersistentVolumeClaim&lt;/code&gt;s that correspond to Cinder volumes.&lt;/p&gt;
&lt;p&gt;Once again, the Cinder CSI driver uses AZ information to label resources and once again it&amp;rsquo;s the &lt;code&gt;Node&lt;/code&gt;s that get the
resulting label. The Cinder CSI driver sets a single label on a node, the &lt;code&gt;topology.cinder.csi.openstack.org/zone&lt;/code&gt;
label:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get Node -o jsonpath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{.items[*].metadata.labels}&amp;#39;&lt;/span&gt; | jq
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;beta.kubernetes.io/arch&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;amd64&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;beta.kubernetes.io/instance-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ci.m1.xlarge&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;beta.kubernetes.io/os&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linux&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;failure-domain.beta.kubernetes.io/region&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;regionOne&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;failure-domain.beta.kubernetes.io/zone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nova&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubernetes.io/arch&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;amd64&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubernetes.io/hostname&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stephenfin-5ps6d-master-0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubernetes.io/os&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;linux&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node-role.kubernetes.io/control-plane&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node-role.kubernetes.io/master&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node.kubernetes.io/instance-type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ci.m1.xlarge&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node.openshift.io/os_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rhcos&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;topology.cinder.csi.openstack.org/zone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nova&amp;#34;&lt;/span&gt;,    &lt;span style=&#34;color:#75715e&#34;&gt;# &amp;lt;--- !!! here !!!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;topology.kubernetes.io/region&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;regionOne&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;topology.kubernetes.io/zone&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;nova&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unlike the other two components we&amp;rsquo;ve talked about though, the Cinder CSI Driver doesn&amp;rsquo;t fetch AZ information from the
API. Instead, it fetches it from the Metadata API, as you can see
&lt;a href=&#34;https://github.com/openshift/cloud-provider-openstack/blob/release-4.15/pkg/csi/cinder/nodeserver.go#L465-L469&#34;&gt;here&lt;/a&gt;.
That &lt;code&gt;NodeGetInfo&lt;/code&gt; function forms part of the CSI spec and is used by &lt;code&gt;kubelet&lt;/code&gt;, as detailed in the &lt;a href=&#34;https://kubernetes-csi.github.io/docs/node-driver-registrar.html&#34;&gt;Kubernetes
documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now this use of the Metadata service is somewhat of an usual choice: the Metadata service is provided by Nova and the AZ
information it exposes is the AZ of the instance (derived from the host the instance is scheduled to). It&amp;rsquo;s therefore a
Compute AZ so why is being used for a storage-related component? The answer is that we&amp;rsquo;re using it because there&amp;rsquo;s
nothing else to use: as described in &lt;a href=&#34;https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-1&#34;&gt;part 1&lt;/a&gt;, OpenStack doesn&amp;rsquo;t provide any mechanism to associate compute
hosts with storage AZs outside of using the same naming scheme across both compute and block storage AZs. In my
experience, this loose coupling is a very frequent source of bugs. To use the topology feature (which we&amp;rsquo;ll go into more
detail on shortly), you really need to have a common set of AZs for both the Compute and Block Storage services. Until
OCP 4.15, the OpenStack Cinder CSI Driver Operator (i.e. the operator that deploys and manages the lifecycle of the
Cinder CSI Driver in an OpenShift deployment) assumed this to be the case and always enabled the topology feature. This
has &lt;a href=&#34;https://github.com/openshift/openstack-cinder-csi-driver-operator/pull/127&#34;&gt;since changed&lt;/a&gt; but if you&amp;rsquo;re running an
older release then you&amp;rsquo;re likely to encounter this issue if e.g. using multiple Nova AZs and single Cinder AZ.&lt;/p&gt;
&lt;p&gt;Making things even more complicated, migration of the Nova instance corresponding to a &lt;code&gt;Node&lt;/code&gt; can result in an instance
moving between AZs, assuming the Nova instance in question was not created in a specific AZ initially. The CSI driver
will detect this change and will attempt to update the labels on the &lt;code&gt;Node&lt;/code&gt;, resulting in the following rather nasty
error that will require manual intervention to solve.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Received NotifyRegistrationStatus call: &amp;amp;RegistrationStatus{PluginRegistered:false,Error:RegisterPlugin error -- plugin registration failed with err: error updating Node object with CSI driver node info: error updating node: timed out waiting for the condition; caused by: detected topology value collision: driver reported &amp;#34;topology.cinder.csi.openstack.org/zone&amp;#34;:&amp;#34;nova&amp;#34; but existing label is &amp;#34;topology.cinder.csi.openstack.org/zone&amp;#34;:&amp;#34;nova-az3&amp;#34;,}
Registration process failed with error: RegisterPlugin error -- plugin registration failed with err: error updating Node object with CSI driver node info: error updating node: timed out waiting for the condition; caused by: detected topology value collision: driver reported &amp;#34;topology.cinder.csi.openstack.org/zone&amp;#34;:&amp;#34;nova&amp;#34; but existing label is &amp;#34;topology.cinder.csi.openstack.org/zone&amp;#34;:&amp;#34;nova-az3&amp;#34;, restarting registration container.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, assuming we know about these issues and work to avoid them, how does one actually use the topology features
provided by the Cinder CSI driver? There are actually two ways. The first is to configure a topology-aware
&lt;code&gt;StorageClass&lt;/code&gt; and use this for a &lt;code&gt;PersistentVolumeClaim&lt;/code&gt;, as seen in the &lt;a href=&#34;https://github.com/kubernetes/cloud-provider-openstack/blob/v1.29.0/examples/cinder-csi-plugin/topology/example.yaml&#34;&gt;examples for the Cinder CSI
Driver&lt;/a&gt;.
For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;storage.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;StorageClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;topology-aware-standard&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;provisioner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cinder.csi.openstack.org&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;volumeBindingMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;WaitForFirstConsumer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;allowedTopologies&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;matchLabelExpressions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;key&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;topology.cinder.csi.openstack.org/zone&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;values&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#ae81ff&#34;&gt;az1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;demo-pvc-with-az&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;accessModes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#ae81ff&#34;&gt;ReadWriteOnce&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;requests&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;storage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1Gi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;storageClassName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;topology-aware-standard&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By using this storage class, we ensure that the Cinder volume created for the PVC will request an availability zone of
&lt;code&gt;az1&lt;/code&gt;. This is the standard mechanism promoted by Kubernetes and is also supported by other non-OpenStack CSI drivers
that provide topology support.&lt;/p&gt;
&lt;p&gt;The other mechanism is to specify a Cinder CSI driver-specific parameter, &lt;code&gt;availability&lt;/code&gt;, when creating the storage
class. This is effectively a legacy option that pre-dates topology support in CSI but it&amp;rsquo;s still available:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;storage.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;StorageClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;legacy-az&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;provisioner&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cinder.csi.openstack.org&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;volumeBindingMode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;WaitForFirstConsumer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;parameters&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;availability&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;az1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;demo-pvc-with-az&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;accessModes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#ae81ff&#34;&gt;ReadWriteOnce&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;requests&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;storage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1Gi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;storageClassName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;legacy-az&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With regards to configuring these labels there&amp;rsquo;s currently nothing to stop you modifying the labels in place, just like
the labels OpenStack CCM sets on &lt;code&gt;Node&lt;/code&gt;s, and like that you don&amp;rsquo;t really want to do this since the Cinder CSI driver
effectively owns them. The only time you may wish to do this is if you&amp;rsquo;ve migrated your Nova instance and hit the issue
described above. In this case, you can opt to manually relabel the &lt;code&gt;Node&lt;/code&gt;, taking care to drain any workload from it
first to avoid topology mismatches.&lt;/p&gt;
&lt;h2 id=&#34;wrap-up&#34;&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;As so concludes this two part series looking at Availability Zones in OpenStack and OpenShift. As you&amp;rsquo;ve hopefully
ascertained, they can be a very useful feature, particularly in larger deployments, but there are more than a few
potential banana skins to be aware of when you start using them for storage. By way of recommendations, I would suggest
either using a single common AZ for you deployment (you can stick to the default of &lt;code&gt;nova&lt;/code&gt;) or a common set of AZs
across both the compute and block storage hosts (e.g. in a two-AZ deployment, you could use &lt;code&gt;az0&lt;/code&gt; and &lt;code&gt;az1&lt;/code&gt; for both
compute and block storage AZs, rather than &lt;code&gt;nova-az0&lt;/code&gt; and &lt;code&gt;nova-az1&lt;/code&gt; for compute AZs and &lt;code&gt;cinder-az0&lt;/code&gt; and &lt;code&gt;cinder-az1&lt;/code&gt;
for block storage AZs). If you insist on sticking with divergent sets of AZs, you should disable the topology feature
and rely on the legacy &lt;code&gt;availability&lt;/code&gt; parameter of the &lt;code&gt;StorageClass&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;I hope this has been useful to someone. If you spot any mistakes or identify things that I should have covered but
didn&amp;rsquo;t, feel free to send me an email and I&amp;rsquo;ll try get things sorted.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>API routing in Openstack</title>
      <link>https://that.guru/blog/routers-in-openstack/</link>
      <pubDate>Wed, 01 May 2024 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/routers-in-openstack/</guid>
      <description>&lt;p&gt;In the Dalamation (2024.2) cycle, we&amp;rsquo;re working on adding OpenAPI schemas for a number of the OpenStack services. As
part of this effort, I&amp;rsquo;ve had to learn more than I would like to know about how various services&amp;rsquo; API machinery works.
The below is a quick summary of how the mapping of URLs (or rather, paths) to API controller methods works in Nova (and
Cinder, Manila and other projects that have copied or inherited Nova&amp;rsquo;s patterns). This is very much inside baseball and
probably not useful outside of OpenStack, since we&amp;rsquo;re using older libraries - namely &lt;a href=&#34;https://pypi.org/project/Routes/&#34;&gt;Routes&lt;/a&gt;, &lt;a href=&#34;https://pypi.org/project/Paste/&#34;&gt;Paste&lt;/a&gt;,
and &lt;a href=&#34;https://pypi.org/project/WebOb/&#34;&gt;WebOb&lt;/a&gt; - that have been mostly superseded by new libraries like Flask, Falcon, or Starlette. Still, maybe
it&amp;rsquo;s useful for someone.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The main entry point for routing in Nova is the &lt;a href=&#34;https://github.com/openstack/nova/blob/29.0.1/nova/api/openstack/compute/routes.py#L846-L884&#34;&gt;&lt;code&gt;APIRouterV21&lt;/code&gt;&lt;/a&gt; class. &lt;code&gt;APIRouterV21&lt;/code&gt; provides mappings
of URLs (or rather, URL paths) to methods of &lt;a href=&#34;https://github.com/openstack/nova/blob/29.0.1/nova/api/openstack/wsgi.py#L378&#34;&gt;&lt;code&gt;Resource&lt;/code&gt;&lt;/a&gt; objects using &lt;code&gt;routes.middleware.RoutesMiddleware&lt;/code&gt;
as the ultimate dispatcher. A &lt;code&gt;Resource&lt;/code&gt; object is a wrapper around some &lt;a href=&#34;https://github.com/openstack/nova/blob/29.0.1/nova/api/openstack/wsgi.py#L750&#34;&gt;&lt;code&gt;Controller&lt;/code&gt;&lt;/a&gt; objects: a main
controller and zero or more sub-controllers. If you look at &lt;a href=&#34;https://github.com/openstack/nova/blob/29.0.1/nova/api/openstack/compute/routes.py&#34;&gt;&lt;code&gt;nova/api/openstack/compute/routes.py&lt;/code&gt;&lt;/a&gt; you&amp;rsquo;ll
see a whole load of &lt;code&gt;functools.partial&lt;/code&gt; calls where we create &lt;code&gt;Resource&lt;/code&gt; objects via calls to &lt;code&gt;_create_controller&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;flavor_controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; functools&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;partial(_create_controller,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    flavors&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;FlavorsController,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        flavor_access&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;FlavorActionController
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, &lt;code&gt;flavors.FlavorsController&lt;/code&gt; is the &amp;ldquo;main&amp;rdquo; controller and &lt;code&gt;flavor_access.FlavorActionController&lt;/code&gt; is a &amp;ldquo;sub&amp;rdquo; (or
&amp;ldquo;action&amp;rdquo;/&amp;ldquo;child&amp;rdquo;) controller. The sub-controller extends the main controller by adding new APIs or actions and to the
best of my knowledge it is legacy from the days where we had API extensions.&lt;/p&gt;
&lt;p&gt;These wrapped controllers are then mapped to paths latter in the same file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ROUTE_LIST &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/flavors/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{id}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;: [flavor_controller, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;show&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;PUT&amp;#39;&lt;/span&gt;: [flavor_controller, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;update&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;DELETE&amp;#39;&lt;/span&gt;: [flavor_controller, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;delete&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/flavors/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{id}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/action&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;: [flavor_controller, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;action&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;APIRouterV21&lt;/span&gt;(base_wsgi&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Router):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, custom_routes&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; path, methods &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; ROUTE_LIST &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; custom_routes:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;# register route to mapper&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now with that knowledge, you can run this script locally to see the generated path-method mappings:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; nova.api.openstack &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; compute
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; oslo_config &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; cfg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; nova.tests &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; fixtures
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CONF &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; cfg&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CONF
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fixtures&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ConfFixture(CONF)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setUp()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;fixtures&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;RPCFixture(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;nova.test&amp;#39;&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;setUp()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;router &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; compute&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;APIRouterV21()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;count &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; route &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; router&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;map&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;matchlist:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;controller&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; route&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;defaults:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; route&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;defaults[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;controller&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wsgi_actions &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; {}:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; action, method &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wsgi_actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;items():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;  action: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;action&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, method: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;method&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;version_select&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; str(method):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            count &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;useless versioned method count: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;count&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All that this is doing is configuring enough config-related fixtures to allow us to create an &lt;code&gt;APIRouterV21&lt;/code&gt; instance so
that we can iterate through the path-controller mappings it has. If you run this, you&amp;rsquo;ll see a whole load of output
finishing in this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;useless versioned method count: 288
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is a count of the number of actions that resolve to &lt;code&gt;version_select&lt;/code&gt; methods. &lt;code&gt;version_select&lt;/code&gt; methods are not the
&amp;ldquo;real&amp;rdquo; method and are instead wrappers around the real methods (potentially plural, depending on amount of
microversioned revisions) that allow us to handle API versioning. This wrapper methods are useless to us in the OpenAPI
work  because we need to get attributes of the real methods - namely, some private attributes we&amp;rsquo;re using to store the
schema used for a given method. The way to find the &amp;ldquo;real&amp;rdquo; method is to look at the &lt;code&gt;versioned_methods&lt;/code&gt; attribute of a
Controller, which contains a mapping of method name to real methods. If you change the above for loop you can see this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;count &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; route &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; router&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;map&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;matchlist:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;controller&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; route&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;defaults:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; route&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;defaults[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;controller&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wsgi_actions &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; {}:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; action, method &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wsgi_actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;items():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        method_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wsgi_actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(action)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; method_name:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            versioned_methods &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; getattr(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;controller, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;versioned_methods&amp;#39;&lt;/span&gt;, {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(method_name)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; versioned_methods:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                method &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; versioned_methods[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;func
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;  action: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;action&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, method: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;method&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;version_select&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; str(method):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            count &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;useless versioned method count: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;count&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now if you run this, you&amp;rsquo;ll get a reduced count:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;useless versioned method count: 224
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, it&amp;rsquo;s still not 0. This is because we&amp;rsquo;re not able to resolve to all &amp;ldquo;real&amp;rdquo; methods using only the &amp;ldquo;main&amp;rdquo;
controller alone. To do that, we need the &lt;code&gt;versioned_methods&lt;/code&gt; attribute of the sub-controllers also, or to use the case
of the controllers we gave at the start, the &lt;code&gt;versioned_methods&lt;/code&gt; attribute of both the &lt;code&gt;FlavorsController&lt;/code&gt; controller
and the &lt;code&gt;FlavorActionController&lt;/code&gt; controller. However, we currently have no reference to &lt;code&gt;FlavorActionController&lt;/code&gt; (or any
other sub controller) so we can&amp;rsquo;t do this.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&#34;https://review.opendev.org/c/openstack/nova/+/915732/&#34;&gt;patch I&amp;rsquo;ve proposed&lt;/a&gt; fixes this so that we store the reference to &lt;code&gt;FlavorActionController&lt;/code&gt; (and any other
sub-controller) under &lt;code&gt;Resource.sub_controllers&lt;/code&gt;, thus giving us a mechanism to retrieve &lt;code&gt;versioned_methods&lt;/code&gt; attributes
(and any other attribute we might need later) from these sub-controller. With this patch applied, we can change the for
loop further:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;count &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; route &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; router&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;map&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;matchlist:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;controller&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; route&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;defaults:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; route&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;defaults[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;controller&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wsgi_actions &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; {}:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; action, method &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wsgi_actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;items():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        main_controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;controller
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        method_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; main_controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wsgi_actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(action)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; method_name:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            versioned_methods &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; getattr(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                main_controller, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;versioned_methods&amp;#39;&lt;/span&gt;, {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(method_name)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; versioned_methods:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                method &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; versioned_methods[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;func
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; sub_controller &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sub_controllers:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            method_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sub_controller&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wsgi_actions&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(action)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; method_name:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                versioned_methods &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; getattr(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    sub_controller, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;versioned_methods&amp;#39;&lt;/span&gt;, {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(method_name)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; versioned_methods:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    method &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; versioned_methods[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;func
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;  action: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;action&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, method: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;method&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;version_select&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; str(method):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            count &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;useless versioned method count: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;count&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this done, we finally get a 0 count:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;useless versioned method count: 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This patch therefore means we&amp;rsquo;ll now be able to inspect elements of the various controller methods. We&amp;rsquo;re planning to
use this as part of the OpenAPI effort to associate a schema with a method so &lt;a href=&#34;https://review.opendev.org/c/openstack/nova/+/915735/&#34;&gt;we can ensure all API methods have
schemas&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Debugging Failed OpenShift-on-OpenStack Deployments</title>
      <link>https://that.guru/blog/debugging-failed-openshift-openstack-deployments/</link>
      <pubDate>Mon, 22 Apr 2024 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/debugging-failed-openshift-openstack-deployments/</guid>
      <description>&lt;p&gt;I deploy OpenShift-on-OpenStack quite regularly these days. Some times these deployments fail and the most common
failure I usually see is a timeout during bootstrapping.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ openshift-install --log-level debug create cluster
DEBUG OpenShift Installer 4.15.10
DEBUG Built from commit 24a827900e76d8f9c79122307415b47a4921bbd7
DEBUG Fetching Metadata...
...
DEBUG Reusing previously-fetched Install Config
INFO Skipping VM console logs gather: no gather methods registered for &amp;#34;openstack&amp;#34;
INFO Pulling debug logs from the bootstrap machine
DEBUG Using SSH_AUTH_SOCK /run/user/1000/keyring/ssh to connect to an existing agent
ERROR Attempted to gather debug logs after installation failure: failed to connect to the bootstrap machine: dial tcp 10.0.212.9:22: connect: connection timed out
ERROR Attempted to gather ClusterOperator status after installation failure: listing ClusterOperator objects: Get &amp;#34;https://api.stephenfin.shiftstack-demo.com:6443/apis/config.openshift.io/v1/clusteroperators&amp;#34;: dial tcp 10.0.214.50:6443: i/o timeout
ERROR Bootstrap failed to complete: timed out waiting for the condition
ERROR Failed to wait for bootstrapping to complete. This error usually happens when there is a problem with control plane hosts that prevents the control plane operators from creating the control plane.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You&amp;rsquo;ve a couple of tools that you can use to validate this. The first of these is to check the serial console.
This will highlight the more egregious issues with your deployment. You can do this with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ openstack console url show stephenfin-5ps6d-bootstrap  &lt;span style=&#34;color:#75715e&#34;&gt;# replace with your own bootstrap server&amp;#39;s name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If this doesn&amp;rsquo;t show anything weird then the next step is to log in to the server and check the status of the &lt;code&gt;bootkube&lt;/code&gt;
service. As is custom with OpenStack, to SSH into a machine you need (a) a floating IP and (b) a security group (or more
accurately a security group rule) that allows SSH access. The Installer automatically assigns a floating IP to the
bootstrap machine so (a) is taken care of. That leaves (b). You like already have an &amp;ldquo;allow SSH&amp;rdquo; security group lying
around and if so, you can use that now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ openstack server add security group stephenfin-5ps6d-bootstrap allow_ssh  &lt;span style=&#34;color:#75715e&#34;&gt;# replace with your own server, SG names&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;If you don&amp;rsquo;t have such a group, creating one is easy. The following ought to do the trick:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ openstack security group create allow_ssh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ openstack security group rule create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --protocol tcp --dst-port &lt;span style=&#34;color:#ae81ff&#34;&gt;22&lt;/span&gt; --remote-ip 0.0.0.0/0 &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    allow_ssh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ openstack security group rule create &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --protocol icmp --remote-ip 0.0.0.0/0 &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    allow_ssh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;Once you&amp;rsquo;ve allowed SSH traffic you can SSH into the machine.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ openstack server ssh stephenfin-5ps6d-bootstrap -- -l core
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;code&gt;core&lt;/code&gt; is the default username for Red Hat CoreOS.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;From here you can follow the directions given in the MOTD and check the &lt;code&gt;bootkube&lt;/code&gt; service first:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ journalctl -b -f -u release-image.service -u bootkube.service
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In my case it appeared the issue was the lack of access to the master nodes:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;s the base image from which all OpenShift Container Platform images inherit.)
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap bootkube.sh[2449]: Check if API and API-Int URLs are reachable during bootstrap
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap bootkube.sh[2449]: Checking if api.stephenfin.shiftstack-demo.com of type API_URL reachable
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap bootkube.sh[2449]: Unable to reach API_URL&amp;#39;s https endpoint at https://api.stephenfin.shiftstack-demo.com:6443/version
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap bootkube.sh[2449]: Unable to validate. https://api.stephenfin.shiftstack-demo.com:6443/version is currently unreachable.
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap bootkube.sh[2449]: Checking if api-int.stephenfin.shiftstack-demo.com of type API_INT_URL reachable
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap bootkube.sh[2449]: Unable to reach API_INT_URL&amp;#39;s https endpoint at https://api-int.stephenfin.shiftstack-demo.com:6443/version
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap bootkube.sh[2449]: Unable to validate. https://api-int.stephenfin.shiftstack-demo.com:6443/version is currently unreachable.
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap bootkube.sh[2449]: bootkube.service complete
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap systemd[1]: bootkube.service: Deactivated successfully.
Apr 22 14:01:09 stephenfin-5ps6d-bootstrap systemd[1]: bootkube.service: Consumed 1min 2.337s CPU time.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The same steps apply for debugging issues with master or worker nodes: add a floating IP, allow SSH access, then SSH
into the machine.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ openstack server add floating ip stephenfin-5ps6d-master-0 10.0.214.101
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ openstack server add security group stephenfin-5ps6d-master-0 allow_ssh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ openstack server ssh stephenfin-5ps6d-master-0 -- -l core
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Availability Zones in Openstack and Openshift (Part 1)</title>
      <link>https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-1/</link>
      <pubDate>Tue, 22 Aug 2023 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-1/</guid>
      <description>
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This is part one of two. If you&amp;rsquo;re looking for part two, you can find it
&lt;a href=&#34;https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-2&#34;&gt;here&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;


&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This post has been updated since publication to add additional information about implicit and explicit AZs.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;After seeing a few too many availability zone-related issues popping up in OpenShift clusters of late, I&amp;rsquo;ve decided it
might make sense to document the situation with OpenStack AZs on OpenShift (and, by extension, Kubernetes). This is the
first of two parts. This part provides some background on what AZs are and how you can configure them, while the &lt;a href=&#34;https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-2&#34;&gt;second
part&lt;/a&gt; examines how AZs affect OpenShift and Kubernetes components such as the OpenStack Machine API Provider,
the OpenStack Cluster API Provider, and the Cinder and Manila CSI drivers.&lt;/p&gt;
&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;Both the Compute (Nova) and Block Storage (Cinder) services in OpenStack support the concept of Availability Zones (AZs)
and the envisioned use cases is very similar for both. Quoting from the &lt;a href=&#34;https://docs.openstack.org/nova/latest/admin/availability-zones.html&#34;&gt;Nova documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Availability Zones are an end-user visible logical abstraction for partitioning a cloud without knowing the physical
infrastructure. They can be used to partition a cloud on arbitrary factors, such as location (country, datacenter,
rack), network layout and/or power source.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Nova documentation then goes on to specifically note that the AZ feature provides no HA benefit in and of itself -
whatever benefits there are are entirely down to how the deployment is designed - thus it&amp;rsquo;s really just a way to signal
something you&amp;rsquo;ve done in your physical deployment. All of this is equally true of both Nova and Cinder, and in my
experience I&amp;rsquo;ve seen AZs used to demarcate both compute and block storage nodes existing on different racks or in
different datacenters.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;The Networking (Neutron) service also has the concept of Availability Zones. However, I&amp;rsquo;m not at all familiar with how
these work and they&amp;rsquo;re not something I&amp;rsquo;ve ever used. As a result, I&amp;rsquo;m not going to cover them here.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;configuring-azs-for-hosts&#34;&gt;Configuring AZs for hosts&lt;/h2&gt;
&lt;p&gt;As you might expect, Cinder AZ&amp;rsquo;s are an attribute of the block storage hosts (i.e. hosts running the &lt;code&gt;cinder-volume&lt;/code&gt;
service). As discussed &lt;a href=&#34;#configuration&#34;&gt;later&lt;/a&gt;, you can configure a host&amp;rsquo;s AZ by setting the &lt;code&gt;[DEFAULT] storage_availability_zone&lt;/code&gt; configuration option in &lt;code&gt;cinder.conf&lt;/code&gt;. By comparison, Nova&amp;rsquo;s AZs are not typically configured
via &lt;code&gt;nova.conf&lt;/code&gt; but are actually attributes of host aggregates and can be configured by setting the &lt;code&gt;availability_zone&lt;/code&gt;
metadata key of an aggregate. If a compute host (i.e. a host running the &lt;code&gt;nova-compute&lt;/code&gt; service) belongs to a host
aggregate with the AZ metadata key set then the host will inherit the AZ of that host aggregate. It&amp;rsquo;s only when a host
doesn&amp;rsquo;t belong to a host aggregate - or none of the host aggregates it belongs to have AZ metadata set - that this
information will be sourced from elsewhere, namely the &lt;code&gt;[DEFAULT] default_availability_zone&lt;/code&gt; config option described
&lt;a href=&#34;#configuration&#34;&gt;below&lt;/a&gt;. Unlike Cinder&amp;rsquo;s config option, this is not intended to differ by host and should be set to the
same value across all compute nodes. Nova will prevent you adding a host to more than one aggregate with AZ metadata set
since a host can only belong to one AZ.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack aggregate create --zone nova-az1 foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack aggregate create --zone nova-az2 bar
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack aggregate add host foo stephenfin-devstack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack aggregate add host bar stephenfin-devstack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ConflictException: 409: Client Error &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url: http://10.0.109.204/compute/v2.1/os-aggregates/13/action, Cannot add host to aggregate 13. Reason: One or more hosts already in availability zone&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;s&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;nova-az1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In addition, if a host has instances on it, the Nova will also prevent you from modifying the AZ metadata of an
aggregate it already belongs to - since this would break the AZ constraint placed on any of the existing instances:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server show test-server -f value -c OS-EXT-AZ:availability_zone -c OS-EXT-SRV-ATTR:host
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nova-az1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stephenfin-devstack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack aggregate show foo -f value -c availability_zone -c hosts
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nova-az1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;stephenfin-devstack&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack aggregate set --zone nova-az2 foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BadRequestException: 400: Client Error &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; url: http://10.0.109.204/compute/v2.1/os-aggregates/12, Cannot update aggregate 12. Reason: One or more hosts contain instances in this zone.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;It should also prevent you from adding/removing a host to/from an aggregate when instances are present but this doesn&amp;rsquo;t
currently happen. This is filed as &lt;a href=&#34;https://bugs.launchpad.net/nova/+bug/1907775&#34;&gt;bug #1907775&lt;/a&gt; and has not yet been
resolved.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;requesting-azs-for-resources-servers-volumes-volume-backups-&#34;&gt;Requesting AZs for resources (servers, volumes, volume backups, &amp;hellip;)&lt;/h2&gt;
&lt;p&gt;Nova allows you to specify an AZ when creating an instance (or &amp;ldquo;server&amp;rdquo;, in OpenStackClient parlance), while Cinder
allows you to specify them when creating a volume, a volume backup, a volume group, or (volume groups&amp;rsquo; deprecated
predecessor) a consistency group. For example, to create an instance (or &amp;ldquo;server&amp;rdquo;) with an explicit compute AZ:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openstack server create --availability-zone compute-az1 ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Likewise, to create a volume and volume backup with an explicit block storage AZ:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openstack volume create --availability-zone volume-az1 ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openstack volume backup create --availability-zone volume-az2 ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, you&amp;rsquo;ll note that these resource types will always have AZ information associated with them, even when an AZ
wasn&amp;rsquo;t specifically requested during creation. This is because, in the absence of specific AZ information, both services
will default to setting the AZ of the resource to the AZ of the host that the resource was created on. Put another way,
if I create instance &lt;code&gt;my-server&lt;/code&gt; with no AZ information and it ends up on host &lt;code&gt;my-host&lt;/code&gt;, then &lt;code&gt;my-server&lt;/code&gt; will inherit
the AZ of &lt;code&gt;my-host&lt;/code&gt;. Block storage resources work in the same way, meaning volume &lt;code&gt;my-volume&lt;/code&gt; will inherit the AZ of the
host it is scheduled to. As a result, there has historically been no way for an end-user to tell if an AZ was explicitly
requested when creating a server or not. In fact, the only way they will find out is if they try to move the server
since Nova will insist of moving the instance to another host within the same AZ (this wouldn&amp;rsquo;t happen for a server that
wasn&amp;rsquo;t explicitly created in an AZ). As we&amp;rsquo;ll touch on in &lt;a href=&#34;https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-2&#34;&gt;part 2&lt;/a&gt;, this has been rather frustrating from an
OpenShift or Kubernetes perspective since Kubernetes&amp;rsquo; topology feature is a hard requirement and it does not like us
changes the AZ-related labels of &lt;code&gt;Node&lt;/code&gt; or &lt;code&gt;Machine&lt;/code&gt; objects, which can happen when you migrate the underlying server
and the server picks up the AZ of the new host. Fortunately, the 2024.1 (Caracal) release of OpenStack introduced a new
field to the &lt;code&gt;GET /servers/{serverID}&lt;/code&gt; response called &lt;code&gt;pinned_availability_zone&lt;/code&gt; which will show the AZ requested
during initial instance creation, if set and it&amp;rsquo;s just a matter of time before we&amp;rsquo;re able to start consuming this in the
various OpenShift and Kubernetes components.&lt;/p&gt;
&lt;h2 id=&#34;combining-nova-and-cinders-az-features&#34;&gt;Combining Nova and Cinder&amp;rsquo;s AZ features&lt;/h2&gt;
&lt;p&gt;Finally, it&amp;rsquo;s worth exploring the interplay of the Nova and Cinder AZ features since this will be particularly relevant
in &lt;a href=&#34;https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-2&#34;&gt;part 2&lt;/a&gt;. In a &lt;a href=&#34;https://access.redhat.com/documentation/en-us/red_hat_openstack_platform/16.2/html-single/hyperconverged_infrastructure_guide/index&#34;&gt;Hyperconverged Infrastructure (HCI)&lt;/a&gt; deployment, where compute and block storage
services run side-by-side on hyperconverged hosts, the compute hosts &lt;em&gt;are&lt;/em&gt; the block storage hosts and there is no
difference between the AZs. In a non-HCI deployment, this is unlikely to be the case but this hasn&amp;rsquo;t prevented people
and applications from frequently munging the two types of AZ, as we will see later. Because this conflation of different
AZ types can happen, the general expectation we would have is that one of the following is true:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;There is only a single compute AZ, a single block storage and they have the same name.&lt;/em&gt; This is the default
configuration if you use &amp;ldquo;stock&amp;rdquo; OpenStack: Nova&amp;rsquo;s default AZ is &lt;code&gt;nova&lt;/code&gt; and Cinder helpfully defaults to the same
value.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;There are multiple compute and block storage AZs, but there is the same number of both and they share the same
name.&lt;/em&gt; For example, both the compute and block storage services have the following AZs defined: &lt;code&gt;AZ0&lt;/code&gt;, &lt;code&gt;AZ1&lt;/code&gt;, and
&lt;code&gt;AZ2&lt;/code&gt;. In this case, users and applications which incorrectly use compute host AZ information to configure the AZ of
volumes and related block storage resources will &amp;ldquo;just work&amp;rdquo;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;There are multiple compute and block storage AZs, and there is either a different number of each or they have
different names.&lt;/em&gt; For example, the compute services have the &lt;code&gt;compute-az0&lt;/code&gt; and &lt;code&gt;compute-az1&lt;/code&gt; AZs defined while the
block storage services have the &lt;code&gt;volume-az0&lt;/code&gt; and &lt;code&gt;volume-az1&lt;/code&gt; AZs defined. In this case, the users and applications
must be very careful to explicitly specify a correct AZ when creating volumes and related block storage resources and
must ensure Nova is configured to allow attaching volumes in other AZs (more of this later too).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;The last case above isn&amp;rsquo;t helped by the fact that neither Nova nor Cinder provide an API to request the correct block
storage AZ for a given compute host. To be fair, such an API would likely be a rather difficult thing to do, given
multiple backends are a thing to be considered. It would be effectively impossible to do automatically, meaning there
would still be initial manual configuration required. The closest analog we have for his today is the &lt;a href=&#34;https://docs.openstack.org/cinder/2023.1/admin/availability-zone-type.html&#34;&gt;Volume Type AZ
feature&lt;/a&gt;, which allows you to indicate the AZs that can be used when creating a volume with a given
volume type (so that e.g. a particular block storage backend that is only available to one rack can&amp;rsquo;t be requested by
volumes hosted by block storage services running on another rack). As the docs for that indicate, this configuration is
entirely deployment specific and therefore totally manual.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;wrap-up&#34;&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;That concludes part 1 of this OpenShift-centric examination of OpenStack Availability Zones. In this part we focused
almost exclusively on OpenStack itself, looking at what AZs are, how they&amp;rsquo;re configured and used, and the various issues
people are likely to encounter along the way, but in &lt;a href=&#34;https://that.guru/blog/availability-zones-in-openstack-and-openshift-part-2&#34;&gt;part 2&lt;/a&gt; we&amp;rsquo;re going to turn our focus to how OpenStack AZs
are consumed and represented by OpenShift components when an OpenShift cluster is deployed on an OpenStack cloud. Stay
tuned!&lt;/p&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference&lt;/h2&gt;
&lt;h3 id=&#34;configuration&#34;&gt;Configuration&lt;/h3&gt;
&lt;p&gt;Since this feature exists across two services, there are two sets of configuration options to be concerned with.&lt;/p&gt;
&lt;p&gt;As of the 2023.1 (Antelope) release, Nova has three relevant configuration options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[DEFAULT] default_availability_zone&lt;/code&gt; defines the default AZ of each compute host, which can be changed by adding the
host to a host aggregate and setting the special &lt;code&gt;availability_zone&lt;/code&gt; metadata property as described in the &lt;a href=&#34;https://docs.openstack.org/nova/latest/admin/availability-zones.html&#34;&gt;nova
docs&lt;/a&gt;. This option defaults to &lt;code&gt;nova&lt;/code&gt; and as noted in the &lt;a href=&#34;https://docs.openstack.org/nova/latest/admin/availability-zones.html&#34;&gt;nova docs&lt;/a&gt;, the default AZ should never
explicitly requesting this AZ when creating new instances since it will prevent migration of instance between
different hosts in different AZs (which is allowed by default if the AZ was unset during initial creation) as well as
identification of hosts that are missing AZ information. You have been warned.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[DEFAULT] default_schedule_zone&lt;/code&gt; defines the default AZ that should be assigned to an instance on creation. If this
is unset, the instance will be assigned an implicit AZ of the host it lands on. You might want to use this if you
wanted the majority of instances to go into a &amp;ldquo;generic&amp;rdquo; AZ while special instances can go into specific AZs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[cinder] cross_az_attach&lt;/code&gt; determines whether volumes are allowed to be attached to an instance if the instance host&amp;rsquo;s
compute AZ differs from that of the volume&amp;rsquo;s block storage AZ. It also determines whether volumes created when
creating a boot-from-volume server have an explicit AZ associated with them or not. This defaults to &lt;code&gt;true&lt;/code&gt; and with
good reason, given the aforementioned caveats around munging of compute and block storage AZs and the need for them to
be identical.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is also the &lt;code&gt;[DEFAULT] internal_service_availability_zone&lt;/code&gt; configuration option, but this has no real impact for
end-users.&lt;/p&gt;
&lt;p&gt;As of the 2023.1 (Antelope) release, Cinder has four relevant configuration options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[DEFAULT] storage_availability_zone&lt;/code&gt; defines the default AZ of the block storage host. This defaults to &lt;code&gt;nova&lt;/code&gt; and
can be overridden on a per-backend basis using &lt;code&gt;[foo] backend_availability_zone&lt;/code&gt;. Speaking of which&amp;hellip;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[foo] backend_availability_zone&lt;/code&gt; define the default AZ for a specific backend of the block storage host. &lt;code&gt;foo&lt;/code&gt; should
be the name of the volume backend, as defined in &lt;code&gt;[DEFAULT] enabled_backends&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[DEFAULT] default_availability_zone&lt;/code&gt; defines the default AZ that should be assigned to a volume on creation. If this
is unset, the volume will be assigned the AZ of the host it lands on (which in turn defaults to &lt;code&gt;[DEFAULT] storage_availability_zone&lt;/code&gt;, per above).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[DEFAULT] allow_availability_zone_fallback&lt;/code&gt; allows you to ignore an request for an invalid block storage AZ and
instead fallback to the default AZ defined in &lt;code&gt;[DEFAULT] default_availability_zone&lt;/code&gt;. This defaults to &lt;code&gt;false&lt;/code&gt;, though
to be honest &lt;code&gt;true&lt;/code&gt; is probably a sensible value for configurations where e.g. there are multiple compute AZs and a
single volume AZ.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;usage&#34;&gt;Usage&lt;/h3&gt;
&lt;p&gt;Again, since this feature exists across two services, there are two sets of resource types to be concerned with.&lt;/p&gt;
&lt;p&gt;To configure the AZ of a compute host, you configure AZ information for a host aggregate and then add the host to this
aggregate.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack aggregate create --zone nova-az1 foo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack aggregate add host foo stephenfin-devstack
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once this is done, you can request the AZ when creating an instance (or &amp;ldquo;server&amp;rdquo;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server create --availability-zone nova-az1 ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On the other hand, the AZ of a storage host is configured via config and there&amp;rsquo;s no API method to configure it. You can
use it when creating a volume just like creating a server though:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openstack volume create --availability-zone volume-az1 ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or when creating a volume backup:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openstack volume backup create --availability-zone volume-az2 ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Other API libraries like gophercloud also expose these attributes and allow them to be configured, but we won&amp;rsquo;t go into
that here.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Listening to notifications in Openstack</title>
      <link>https://that.guru/blog/notifications-in-openstack/</link>
      <pubDate>Fri, 07 Jul 2023 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/notifications-in-openstack/</guid>
      <description>&lt;p&gt;Another short one. As mostly anyone that has had to maintain an OpenStack cluster will know, many of the OpenStack
services has the ability to report notifications via the message bus. These notifications will be enabled in most
production deployments but DevStack doesn&amp;rsquo;t enable them by default. Fortunately enabling them is pretty painless. If we
wanted to enable the notifications in, say, Keystone, we can do so by configuring the following in
&lt;code&gt;/etc/keystone/keystone.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[oslo_messaging_notifications]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;transport_url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;rabbit://stackrabbit:***@***:5672/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;driver&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;messagingv2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These options are provided by the &lt;a href=&#34;https://docs.openstack.org/oslo.messaging/latest/&#34;&gt;oslo.messaging&lt;/a&gt; project, which virtually every OpenStack service
harnesses.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve enabled them though, you what can you do with them? Using the &lt;code&gt;messagingv2&lt;/code&gt; means they&amp;rsquo;re published over the
AMQP message bus (and the message is enveloped, which is the difference to &lt;code&gt;messaging&lt;/code&gt;), however, they&amp;rsquo;re not exposed
via HTTP APIs or similar. We could whip out a telemetry tool like &lt;a href=&#34;https://docs.openstack.org/ceilometer/latest/&#34;&gt;Ceilometer&lt;/a&gt; but that seems overkill for a
quick bit of debugging. Well it turns out &lt;em&gt;oslo.messaging&lt;/em&gt; provides utilities for both creating notifications &lt;strong&gt;and&lt;/strong&gt;
listening to them. Writing a listener is relatively simple:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; logging
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; oslo_config &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; cfg
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; oslo_messaging
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cfg&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CONF()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;basicConfig(level&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;INFO)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LOG &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; logging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;getLogger()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;URL &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;rabbit://stackrabbit:***@***:5672/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NotificationEndpoint&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_notify&lt;/span&gt;(self, ctxt, publisher_id, event_type, payload, metadata):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        LOG&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;notification received &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; (publisher_id, event_type))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        output &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;payload&amp;#34;&lt;/span&gt;: payload,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;publisher_id&amp;#34;&lt;/span&gt;: publisher_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;event_type&amp;#34;&lt;/span&gt;: event_type,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            indent&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(output)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    audit &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _notify
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    debug &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _notify
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    info &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _notify
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    warn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _notify
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _notify
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    critical &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _notify
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sample &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _notify
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    transport &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; oslo_messaging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_notification_transport(cfg&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CONF, url&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;URL)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    targets &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [oslo_messaging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Target(topic&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;notifications&amp;#39;&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    endpoints &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [NotificationEndpoint()]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    server &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; oslo_messaging&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_notification_listener(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        transport,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        targets,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        endpoints,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        executor&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;threading&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    LOG&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;messaging starting&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    server&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;start()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    LOG&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;messaging started&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    server&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;wait()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    LOG&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;info(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;exit&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;Don&amp;rsquo;t forget to update &lt;code&gt;URL&lt;/code&gt; to match the value of your services&#39;
&lt;a href=&#34;https://docs.openstack.org/oslo.messaging/latest/configuration/opts.html#oslo_messaging_notifications.transport_url&#34;&gt;&lt;code&gt;[oslo_messaging_notifications] transport_url&lt;/code&gt;&lt;/a&gt;. You might also need to modify the target topic or
add additional targets if any of your services override &lt;a href=&#34;https://docs.openstack.org/oslo.messaging/latest/configuration/opts.html#oslo_messaging_notifications.topics&#34;&gt;&lt;code&gt;[oslo_messaging_notifications] topics&lt;/code&gt;&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;If you run this and then, say, attempt to authenticate with invalid credentials, you&amp;rsquo;ll see notifications arriving:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;payload&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;typeURI&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://schemas.dmtf.org/cloud/audit/1.0/event&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;eventType&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;activity&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;68c5b24b-89f0-5c93-abe4-5c3d6651fb00&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;eventTime&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2023-07-07T15:52:00.496156+0000&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;authenticate&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;outcome&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;failure&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;observer&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;08d95fde312344debbcc00106b139995&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;typeURI&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;service/security&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;initiator&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;78fab588d9d34900a6c59fc22f3a2049&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;typeURI&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;service/security/account/user&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;host&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;address&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;***&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;openstacksdk/1.3.0 keystoneauth1/5.2.1 python-requests/2.31.0 CPython/3.11.4&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;request_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;req-0126be50-db3a-4975-a814-d717a5fdfdf8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;***&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;***&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;target&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fc220ce7-2787-5152-bf81-9943cb8ee24b&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;typeURI&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;service/security/account/user&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;publisher_id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;identity.stephenfin-devstack&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;event_type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;identity.authenticate&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This can be extremely valuable for debugging since notifications will expose things that aren&amp;rsquo;t necessarily obvious from
logs.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Multipoint VXLAN Tunnels</title>
      <link>https://that.guru/blog/multipoint-vxlan-tunnels/</link>
      <pubDate>Mon, 26 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/multipoint-vxlan-tunnels/</guid>
      <description>&lt;p&gt;I recently had to deploy a multi-node OpenStack cluster in Azure. Like most cloud platforms, Azure does not expose L2
networks and implements MAC spoofing protection, preventing VMs from advertising IPs that have not been assigned to
them. However, we want a &lt;a href=&#34;https://opensource.com/article/17/4/openstack-neutron-networks&#34;&gt;provider network&lt;/a&gt; in our deployment in order to provide external network
connectivity to OpenStack VMs, which meant we needed some kind of L2 network. Enter overlay networks, specifically VXLAN
network, which will let us provide a virtual L2 domain for our created VMs. We ended up not needing this as we only
deployed a single networking controller node, but it was an interesting exercise all the same. I&amp;rsquo;ve documented the steps
I needed to take to get this functioning below.&lt;/p&gt;
&lt;h2 id=&#34;the-scenario&#34;&gt;The scenario&lt;/h2&gt;
&lt;p&gt;We have three hosts: a bastion and two OpenStack hosts. We would like to configure a VXLAN mesh between all three VMs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bastion: &lt;code&gt;192.168.226.4&lt;/code&gt; (VXLAN IP: &lt;code&gt;192.168.234.1/23&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Host 00: &lt;code&gt;192.168.226.5&lt;/code&gt; (VXLAN IP: &lt;code&gt;192.168.234.2/23&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Host 01: &lt;code&gt;192.168.226.6&lt;/code&gt; (VXLAN IP: &lt;code&gt;192.168.234.3/23&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;On &lt;code&gt;bastion&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link add provider0 type vxlan id &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt; local 192.168.226.4 dstport &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bridge fdb append to 00:00:00:00:00:00 dst 192.168.226.5 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# host-00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bridge fdb append to 00:00:00:00:00:00 dst 192.168.226.6 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# host-01&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip addr add 192.168.234.1/23 dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link set up dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On &lt;code&gt;host-00&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link add provider0 type vxlan id &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt; local 192.168.226.5 dstport &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bridge fdb append to 00:00:00:00:00:00 dst 192.168.226.4 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# bastion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bridge fdb append to 00:00:00:00:00:00 dst 192.168.226.6 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# host-01&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip addr add 192.168.234.2/23 dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link set up dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On &lt;code&gt;host-01&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link add provider0 type vxlan id &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt; local 192.168.226.6 dstport &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bridge fdb append to 00:00:00:00:00:00 dst 192.168.226.4 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# bastion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bridge fdb append to 00:00:00:00:00:00 dst 192.168.226.5 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# host-00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip addr add 192.168.234.3/23 dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link set up dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-alternative-solution&#34;&gt;The (alternative) solution&lt;/h2&gt;
&lt;p&gt;If using Debian or Ubuntu, you can achieve part of this using netplan. This can be helpful when using e.g. Ansible.
If using netplan, simply omit the &lt;code&gt;network.tunnels.{name}.remote&lt;/code&gt; setting, like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;network&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;tunnels&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;provider0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;mode&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;vxlan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;local&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.226.4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;addresses&lt;/span&gt;: [ &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.234.1&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/23 ]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;version&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You then need to run the &lt;code&gt;bridge fdb&lt;/code&gt; commands manually as it doesn&amp;rsquo;t seem to be possible to configure this via netplan.&lt;/p&gt;
&lt;h2 id=&#34;the-explanation&#34;&gt;The explanation&lt;/h2&gt;
&lt;p&gt;Typically, you would create a VXLAN tunnel endpoint (VTEP) using a command like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link add provider0 type vxlan id &lt;span style=&#34;color:#ae81ff&#34;&gt;16&lt;/span&gt; local 192.168.226.4 remote 192.168.226.5 dstport &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip addr add 192.168.234.1/23 dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, the &lt;code&gt;ip&lt;/code&gt; command does not allow you to specify multiple &lt;code&gt;remote&lt;/code&gt; stanzas. As a result, you need to omit this
and manually specify forwarding database entries using the &lt;code&gt;bridge fdb&lt;/code&gt; command.&lt;/p&gt;
&lt;h2 id=&#34;bonus-using-gre-instead&#34;&gt;BONUS! Using GRE instead&lt;/h2&gt;
&lt;p&gt;This wasn&amp;rsquo;t an option for me because GRE &lt;a href=&#34;https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-faq&#34;&gt;is apparently blocked by Azure&lt;/a&gt;. In theory though, there&amp;rsquo;s no
reason this shouldn&amp;rsquo;t work otherwise.&lt;/p&gt;
&lt;p&gt;On &lt;code&gt;bastion&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip tunnel add provider0 mode gre local 192.168.226.4 key &lt;span style=&#34;color:#ae81ff&#34;&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip addr add 192.168.234.1/23 dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip neighbor add 192.168.234.2 lladdr 192.168.226.5 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# host-00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip neighbor add 192.168.234.3 lladdr 192.168.226.6 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# host-01&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link set dev provider0 up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On &lt;code&gt;host-00&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip tunnel add provider0 mode gre local 192.168.226.5 key &lt;span style=&#34;color:#ae81ff&#34;&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip addr add 192.168.234.2/23 dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip neighbor add 192.168.234.1 lladdr 192.168.226.4 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# bastion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip neighbor add 192.168.234.3 lladdr 192.168.226.6 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# host-01&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link set dev provider0 up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On &lt;code&gt;host-01&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip tunnel add provider0 mode gre local 192.168.226.6 key &lt;span style=&#34;color:#ae81ff&#34;&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip addr add 192.168.234.3/23 dev provider0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip neighbor add 192.168.234.1 lladdr 192.168.226.4 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# bastion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip neighbor add 192.168.234.2 lladdr 192.168.226.5 dev provider0  &lt;span style=&#34;color:#75715e&#34;&gt;# host-00&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip link set dev provider0 up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://vincent.bernat.ch/en/blog/2017-vxlan-linux&#34;&gt;VXLAN &amp;amp; Linux - Vincent Bernat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://joejulian.name/post/how-to-configure-linux-vxlans-with-multiple-unicast-endpoints/&#34;&gt;How to configure linux vxlans with multiple unicast endpoints - Joe Julian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mcastelino.medium.com/setting-up-a-overlay-network-using-point-to-multi-point-gre-tunnels-c8c2422c2fa2&#34;&gt;Setting up a overlay network using point to multi-point GRE tunnels - M Castelino&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.madebymikal.com/setting-up-vxlan-between-nested-virt-vms-on-google-compute-engine/&#34;&gt;Setting up VXLAN between nested virt VMs on Google Compute Engine - Michael Still&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/configuring_and_managing_networking/assembly_using-a-vxlan-to-create-a-virtual-layer-2-domain-for-vms_configuring-and-managing-networking&#34;&gt;Chapter 10. Using a VXLAN to create a virtual layer-2 domain for VMs - RHEL 9 Networking Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.rdoproject.org/networking/networking-in-too-much-detail/#network-host-external-traffic-kl&#34;&gt;Networking in too much detail - RDO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Deploying MetalLB with BGP on Openstack (Part 2)</title>
      <link>https://that.guru/blog/deploying-metallb-on-openstack-part-2/</link>
      <pubDate>Tue, 23 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/deploying-metallb-on-openstack-part-2/</guid>
      <description>
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This is part two of two. If you&amp;rsquo;re looking for part one, you can find it
&lt;a href=&#34;https://that.guru/blog/deploying-metallb-on-openstack-part-1&#34;&gt;here&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;As noted &lt;a href=&#34;https://that.guru/blog/deploying-metallb-on-openstack-part-1&#34;&gt;previously&lt;/a&gt;, one of the goals for an upcoming OpenShift release is to formally support
&lt;a href=&#34;https://metallb.universe.tf/&#34;&gt;MetalLB&lt;/a&gt; and the &lt;a href=&#34;https://github.com/metallb/metallb-operator&#34;&gt;MetalLB operator&lt;/a&gt; on the OpenStack platform. In part one of the series,
we configured an environment with OpenStack, OpenShift and BGP software routers. Now, in part two, we&amp;rsquo;re going to focus
on installing and configuring MetalLB itself.&lt;/p&gt;
&lt;h2 id=&#34;install-metallb&#34;&gt;Install MetalLB&lt;/h2&gt;
&lt;p&gt;The first step on the path to using MetalLB is actually installing it. As a reminder, we want to use MetalLB in &lt;em&gt;BGP
mode&lt;/em&gt;. This necessitates things like routers that speak BGP and an OpenStack deployment that is configured to talk to
these routers. These were all discussed in &lt;a href=&#34;https://that.guru/blog/deploying-metallb-on-openstack-part-1&#34;&gt;part one&lt;/a&gt; of this series, and if you followed along with this then
you will currently have a deployment that looks like this:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://that.guru/media/deploying-metallb-on-openstack-1.png&#34;
    alt=&#34;Image displaying the network topology of VMs in a deployment&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;Our deployment configured&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;If this is not the case, you probably want to read that part first. However, assuming it is, we can now proceed with
installation. As a Kubernetes-native project, MetalLB comes with all the usual mechanisms for installation of Kubernetes
components. Plain manifests are provided, as are Helm Charts and an operator, the MetalLB Operator. The various
installation mechanisms are all discussed in the &lt;a href=&#34;https://metallb.universe.tf/installation/&#34;&gt;MetalLB installation guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Because operators are awesome, I opted to use the MetalLB Operator to deploy MetalLB and manage its lifecycle. The
MetalLB Operator is available on OperatorHub at operatorhub.io/operator/metallb-operator but when I was testing, the
&lt;code&gt;main&lt;/code&gt; branch contained a feature I wanted, namely the ability to configure a &lt;a href=&#34;https://github.com/metallb/metallb-operator/pull/342&#34;&gt;&lt;code&gt;loadBalancerClass&lt;/code&gt;&lt;/a&gt;. As a
result, I opted to deploy MetalLB Operator from source. The MetalLB Operator provides a very helpful &lt;code&gt;Make&lt;/code&gt; target to do
this, which you can use:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ git clone https://github.com/metallb/metallb-operator
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ cd metallb-operator
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ make deploy-openshift
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Deployment takes a while but once finished we can validate that everything is running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get all -n metallb-system
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Installation complete! Now onto configuration.&lt;/p&gt;
&lt;h2 id=&#34;initial-metallb-configuration&#34;&gt;Initial MetalLB configuration&lt;/h2&gt;
&lt;p&gt;With the MetalLB Operator installed, it&amp;rsquo;s time to &lt;em&gt;configure&lt;/em&gt; MetalLB. By using MetalLB Operator we gain the ability to
manage configuration of MetalLB itself via CRs. First up is the &lt;code&gt;MetalLB&lt;/code&gt; CR. This is primary configuration mechanism
and is the thing that enables MetalLB itself. There should only be one of them, which you can create like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ cat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;&amp;lt; EOF | oc apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;apiVersion: metallb.io/v1beta1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;kind: MetalLB
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;metadata:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    name: metallb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    namespace: metallb-system
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;spec:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    loadBalancerClass: &amp;#39;metallb.universe.tf/metallb&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    nodeSelector:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        kubernetes.io/os: linux
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        node-role.kubernetes.io/worker: &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are a couple of things to note here. Firstly, we are configuring &lt;code&gt;spec.loadBalancerClass&lt;/code&gt;. This is necessary
because &lt;em&gt;dev-install&lt;/em&gt; deploys Octavia by default and &lt;code&gt;cloud-provider-openstack&lt;/code&gt; is using this for load balancing by
default. By setting this, we have the ability to later configure &lt;code&gt;Service&lt;/code&gt;s to use MetalLB rather than Octavia.
Secondly, we are restricting the speakers to run on worker nodes by configuring &lt;code&gt;spec.nodeSelector&lt;/code&gt; as there&amp;rsquo;s simply no
need to run them on the master nodes.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;The choice of &lt;code&gt;metallb.universe.tf/metallb&lt;/code&gt; as the &lt;code&gt;spec.loadBalancerClass&lt;/code&gt; is totally arbitrary. You can use anything
label-like here so long as you use the same value later when creating &lt;code&gt;Service&lt;/code&gt;s.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;Once the CR is created, we can validate that it exists:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get -n metallb-system metallb metallb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Assuming so, you can ensure the &lt;code&gt;loadBalancerClass&lt;/code&gt; attribute is in effect by inspecting the underlying service
containers:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get -n metallb-system pods
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME                                                  READY   STATUS    RESTARTS      AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;controller-7df7bbcffb-8cqzb                           1/1     Running   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;             5m1s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;metallb-operator-controller-manager-c44c967b9-l6rvx   1/1     Running   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;             13h
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;metallb-operator-webhook-server-6fdccfb5c5-k8b2m      1/1     Running   &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;13h ago&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;   13h
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;speaker-bg6pq                                         4/4     Running   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;             5m1s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;speaker-q6dmg                                         4/4     Running   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;             5m1s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;speaker-sjmtc                                         4/4     Running   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;             5m1s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;speaker-z9zrr                                         4/4     Running   &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;             5m1s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get -n metallb-system pod controller-7df7bbcffb-8cqzb -o jsonpath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{.spec.containers[0].args}&amp;#34;&lt;/span&gt; | yq -P
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- --port&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;7472&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- --log-level&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;info
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- --cert-service-name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;webhook-service
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- --lb-class&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;metallb.universe.tf/metallb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- --webhook-mode&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;disabled
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get -n metallb-system pod speaker-bg6pq -o jsonpath&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{.spec.containers[0].args}&amp;#34;&lt;/span&gt; | yq -P
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- --port&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;7472&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- --log-level&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;info
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- --lb-class&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;metallb.universe.tf/metallb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This all looks good. With this, the initial configuration of MetalLB is also complete. Now to the next step: configuring
MetalLB for our BGP environment.&lt;/p&gt;
&lt;h2 id=&#34;configuring-metallb-to-talk-to-the-router&#34;&gt;Configuring MetalLB to talk to the router&lt;/h2&gt;
&lt;p&gt;MetalLB in BGP mode requires a few bits of information. It needs a list of IP addresses that it can hand out, it needs
information about the routers that it should peer with, and it needs to be told which IP addresses it can advertise via
BGP. This configuration is all done using CRs, namely the &lt;code&gt;IPAddressPool&lt;/code&gt;, &lt;code&gt;BGPPeer&lt;/code&gt;, and &lt;code&gt;BGPAdvertisement&lt;/code&gt; CRs. First
up, the IP address pools.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;&amp;lt; EOF | oc apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;apiVersion: metallb.io/v1beta1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;kind: IPAddressPool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;metadata:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  namespace: metallb-system
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  name: ipaddresspool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;spec:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  addresses:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    - 192.168.50.1-192.168.50.254
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve picked a totally arbitrary IP address range, ensuring it doesn&amp;rsquo;t overlap with any other IP address range on the
network. Obviously if you have specific IP addresses you wish to use, you should configure these instead. I&amp;rsquo;ve also only
configured one. This should be loads for our purposes.&lt;/p&gt;
&lt;p&gt;Next, the BGP peers. While we have multiple leaf routers, our master and worker nodes are all talking to the
&lt;code&gt;rack1-gateway&lt;/code&gt; router. As a result, we really only need to create a BGP peer for this router.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;&amp;lt; EOF | oc apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;apiVersion: metallb.io/v1beta2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;kind: BGPPeer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;metadata:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  name: rack1-bgp-peer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  namespace: metallb-system
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;spec:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  myASN: 64998
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  peerASN: 64999
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  peerAddress: 192.168.10.1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  password: &amp;#34;f00barZ&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;poc-bgp&lt;/code&gt; project we used to configure our BGP software router uses the &lt;code&gt;64999&lt;/code&gt; ASN for all of the leaf routers and
configured a password of &lt;code&gt;f00barZ&lt;/code&gt;. Since we&amp;rsquo;re pairing with one of these leaf routers, &lt;code&gt;rack1-gateway&lt;/code&gt;, we needed to
configure these. We chose the &lt;code&gt;64998&lt;/code&gt; ASN for ourselves.&lt;/p&gt;
&lt;p&gt;Finally, the BGP advertisement. Because MetalLB supports both a BGP and an L2 mode, it is possible that you could have
some IP addresses that are meant to be assigned using BGP and other IP addresses that are meant to be assigned using
ARP. In our case, we are only configuring MetalLB in BGP mode and only have a single IP address pool, which means we can
advertise all IPs via BGP.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;&amp;lt; EOF | oc apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;apiVersion: metallb.io/v1beta1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;kind: BGPAdvertisement
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;metadata:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  name: bgpadvertisement
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  namespace: metallb-system
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With those three CRs created, we can validate that they have been in fact created.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get -n metallb-system ipaddresspool
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME            AUTO ASSIGN   AVOID BUGGY IPS   ADDRESSES
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ipaddresspool   true          false             &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;192.168.50.1-192.168.50.254&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get -n metallb-system bgppeer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME             ADDRESS        ASN     BFD PROFILE   MULTI HOPS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rack1-bgp-peer   192.168.10.1   &lt;span style=&#34;color:#ae81ff&#34;&gt;64999&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get -n metallb-system bgpadvertisement
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME               IPADDRESSPOOLS   IPADDRESSPOOL SELECTORS   PEERS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bgpadvertisement
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wonderful!&lt;/p&gt;
&lt;h2 id=&#34;dealing-with-port-security-issues&#34;&gt;Dealing with port security issues&lt;/h2&gt;
&lt;p&gt;With the above steps completed, our configuration of MetalLB is complete. However, if you were to create a &lt;code&gt;Service&lt;/code&gt;
with &lt;code&gt;type=LoadBalancer&lt;/code&gt; now, you would find it doesn&amp;rsquo;t actually work. This is because Neutron has MAC spoofing
protection that is enabled by default. The fact that our worker node is advertising IP addresses that neutron does not
know about triggers this protection and results in packets getting dropped as they egress our worker node.&lt;/p&gt;
&lt;p&gt;To work around this issue you have two options: you can disable port security, or you make use of neutron&amp;rsquo;s
&lt;code&gt;allowed-address-pairs&lt;/code&gt; extension to allow additional IPs, subnets, and MAC addresses, other than the fixed IP and MAC
address associated with the port, to act as source addresses for traffic leaving the port/virtual interface. Let&amp;rsquo;s
start with the former.&lt;/p&gt;
&lt;h3 id=&#34;disable-port-security&#34;&gt;Disable port security&lt;/h3&gt;
&lt;p&gt;Disabling port security requires removing any existing allowed address pairs, removing any security groups, and finally
disabling port security in general. If you you opt to do this, you will need to do this it for all worker node ports. As
we only only have one worker node here, there is only one port to worry about, but you can trivially script the removal
of port security for a larger number of ports using &lt;em&gt;openstackclient&lt;/em&gt; or something like Ansible. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ports&lt;span style=&#34;color:#f92672&#34;&gt;=(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stephenfin-qfnvm-worker-0-97fkv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; port in &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;ports[@]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;; &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    openstack port set --no-allowed-address &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$port&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    openstack port set --no-security-group &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$port&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    openstack port set --disable-port-security &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$port&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;I&amp;rsquo;ve used a static list of ports here. You can find this list of ports by filtering on the &lt;code&gt;openshiftClusterID=foo&lt;/code&gt; tag,
where &lt;code&gt;foo&lt;/code&gt; is the name &lt;code&gt;openshift-installer&lt;/code&gt; (or rather, Terraform) assigned to the cluster. Don&amp;rsquo;t forget to skip the
ingress and API ports!&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;This is definitely the simpler option, though you will obviously be reliant on something else to provide network
security. Let&amp;rsquo;s look at the alternative option.&lt;/p&gt;
&lt;h3 id=&#34;configure-allowed-address-pairs&#34;&gt;Configure allowed address pairs&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;allowed-address-pairs&lt;/code&gt; extension can be used to allow egress traffic from a VM with an IP outside of the one
configured on the neutron port. From the docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;allowed-address-pairs&lt;/code&gt; extension adds an &lt;code&gt;allowed_address_pairs&lt;/code&gt;
attribute to ports. The value of &lt;code&gt;allowed_address_pairs&lt;/code&gt; is an array of
allowed address pair objects, each having an &lt;code&gt;ip_address&lt;/code&gt; and a
&lt;code&gt;mac_address&lt;/code&gt;. The set of allowed address pairs defines IP and MAC address
that the port can use when sending packets if &lt;code&gt;port_security_enabled&lt;/code&gt; is
&lt;code&gt;true&lt;/code&gt; (see the &lt;code&gt;port-security&lt;/code&gt; extension). Note that while the
&lt;code&gt;ip_address&lt;/code&gt; is required in each allowed address pair, the &lt;code&gt;mac_address&lt;/code&gt;
is optional and will be taken from the port if not specified.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you inspect the ports assigned to the master and worker nodes, you&amp;rsquo;ll note that we already have some allowed address
pairs defined. OpenShift on OpenStack uses this feature to enable load balancing of the ingress and API VIPs and we can
opt to use it for load balancing of services also. Unfortunately, there is no easy way to create allowed address pairs
for all IP addresses in a given subnet, nor to create them in a manner where they apply to all hosts. As a result,
applying this will be tedious and scalability may well become a concern, particularly where there is a large number of
worker nodes or a large range of IP addresses (via &lt;code&gt;IPAddressPool&lt;/code&gt;). Since we only have one node and will only create
one example, we can at least try it out.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack port set --allowed-address&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;ip-address&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.50.1 stephenfin-qfnvm-worker-0-97fkv-0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you wanted to create multiple services, you would need to create an allowed address pair for each and every IP
address specified in the &lt;code&gt;IPAddressPool&lt;/code&gt;(s).&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;The upcoming Bobcat release of neutron adds a new &lt;code&gt;allowedaddresspairs-atomic&lt;/code&gt; extension, which provides new
&lt;code&gt;add_allowed_address_pairs&lt;/code&gt; and &lt;code&gt;remove_allowed_address_pairs&lt;/code&gt; member actions. Once released, you could use this to bulk
update the allowed-address-pairs in an atomic manner, which would be helpful if you wanted to write a tool to automate
the configuration of allowed address pairs where there are large numbers of possible IP addresses. You will still have
to specify all possible IP addresses individually, however.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;This is the more involved option but either option should work. In any case, with port security issues mitigated, we are
finally in a position to validate behavior.&lt;/p&gt;
&lt;h2 id=&#34;testing-it-out&#34;&gt;Testing it out&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s create a simple &amp;ldquo;Hello, world&amp;rdquo; example to test this out. We&amp;rsquo;ll use the &lt;code&gt;e2e-test-images/agnhost&lt;/code&gt; image to do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc new-project test-metallb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc create deployment hello-node --image&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;k8s.gcr.io/e2e-test-images/agnhost:2.33 -- /agnhost serve-hostname
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ cat  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;&amp;lt; EOF | oc apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;apiVersion: v1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;kind: Service
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;metadata:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  name: test-frr
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;spec:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  selector:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    app: hello-node
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  ports:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    - port: 80
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      protocol: TCP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;      targetPort: 9376
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  loadBalancerClass: metallb.universe.tf/metallb
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  type: LoadBalancer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should look very familiar if you&amp;rsquo;ve ever worked with services. In fact, there&amp;rsquo;s nothing unusual here aside from our
use of &lt;code&gt;spec.type=LoadBalancer&lt;/code&gt; and the declaration of &lt;code&gt;spec.loadBalancerClass&lt;/code&gt;. The former ensure we actually use a
load balancer while the latter ensures that the load balancer used is MetalLB rather than Octavia.&lt;/p&gt;
&lt;p&gt;Once created, inspect the service to find the IP address assigned:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ oc get service test-frr
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME       TYPE           CLUSTER-IP       EXTERNAL-IP    PORT&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;S&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;        AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;test-frr   LoadBalancer   172.30.130.128   192.168.50.1   80:32519/TCP   2m13s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From this, we can see that the service has been assigned IP &lt;code&gt;192.168.50.1&lt;/code&gt;. If we &lt;code&gt;curl&lt;/code&gt; this, we should get something
back. Let&amp;rsquo;s test it out.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ curl 192.168.50.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hello-node-855787d74c-fkbt5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And it works. Good job, people!&lt;/p&gt;
&lt;h2 id=&#34;wrap-up&#34;&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we deployed MetalLB via the MetalLB Operator, configured it a minimal manner, and worked around the port
security issues that using an external routing solution with neutron presents. As you can see though, port security
issues aside, the actual process of deploying and configuring MetalLB is rather effortless.&lt;/p&gt;
&lt;p&gt;In a future post, I plan to outline some of the steps I used to debug and resolve issues I had deploying this initial
configuration (there were a few). For now, I hope this was helpful to someone.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Deploying MetalLB with BGP on Openstack (Part 1)</title>
      <link>https://that.guru/blog/deploying-metallb-on-openstack-part-1/</link>
      <pubDate>Mon, 22 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/deploying-metallb-on-openstack-part-1/</guid>
      <description>
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This is part one of two. If you&amp;rsquo;re looking for part two, you can find it
&lt;a href=&#34;https://that.guru/blog/deploying-metallb-on-openstack-part-2&#34;&gt;here&lt;/a&gt;.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;One of the goals for an upcoming OpenShift release is to formally support &lt;a href=&#34;https://metallb.universe.tf/&#34;&gt;MetalLB&lt;/a&gt; and the &lt;a href=&#34;https://github.com/metallb/metallb-operator&#34;&gt;MetalLB
operator&lt;/a&gt; on the OpenStack platform. While MetalLB is mainly targeted at bare metal deployments, it
also has value on some on-prem platforms such as OpenStack; for example, if you can&amp;rsquo;t or don&amp;rsquo;t want to deploy the
OpenStack Load Balancing service (Octavia). I&amp;rsquo;ve been investigating how this would work and this post consists of my
testing notes, along with some asides to help flesh things out. I took a few shortcuts, particularly when it comes to
initial deployment, so I don&amp;rsquo;t know how broadly useful this will be. However, there are very few blogs talking about
MetalLB &lt;strong&gt;in BGP mode&lt;/strong&gt; on OpenStack so I imagine there&amp;rsquo;s &lt;em&gt;something&lt;/em&gt; of use here.&lt;/p&gt;
&lt;p&gt;One thing to note from the get-go is that this focuses on using MetalLB as a load balancer for &lt;strong&gt;applications&lt;/strong&gt; or data
plane - that is, &lt;code&gt;Service&lt;/code&gt; instances with &lt;code&gt;type=LoadBalancer&lt;/code&gt;. Like OpenShift on bare metal, OpenShift on OpenStack uses
&lt;em&gt;keepalived&lt;/em&gt; and &lt;em&gt;HAProxy&lt;/em&gt; to load balance the API and ingress VIPs (which you can see in action by inspecting the pods
in &lt;code&gt;openshift-openstack-infra&lt;/code&gt; namespace). There are no current plans to provide a mechanism for using MetalLB to load
balance the control plane.&lt;/p&gt;
&lt;p&gt;This is the first part of two and focuses on deploying OpenStack, configuring a BGP environment, before deploying an
OpenShift cluster. The end result should look something like this:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://that.guru/media/deploying-metallb-on-openstack-1.png&#34;
    alt=&#34;Image displaying the network topology of VMs in a deployment&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;Our proposed deployment&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This is likely the most involved section to do from scratch owing to the variety and varying complexity of options
available for configuring OpenStack, BGP, and OpenShift. It is also heavily dependent on the hardware you have available
to you. As a result, you may wish to ignore this part entirely and figure out your own mechanism for getting an
environment that looks like the above.&lt;/p&gt;
&lt;h2 id=&#34;openstack&#34;&gt;OpenStack&lt;/h2&gt;
&lt;p&gt;Before we even start thinking about OpenShift, we need our OpenStack platform to run things on. You may well have an
OpenStack platform already, in which case you can skip this section entirely. Similarly, you may have a preferred
tool or workflow for deploying OpenStack clusters, meaning again you can skip this section entirely and do your own
thing. The important thing is that whatever environment you have must have enough capacity to run 4 small instances for
BGP routing (a flavour with 1GB RAM, 1 vCPU, and 10GB disk will suffice) and 4 much larger instances for OpenShift nodes
(16GB RAM, 4 vCPU and 40GB disk &lt;em&gt;minimum&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;In my case, I was deploying OpenStack from scratch onto a single beefy bare metal machine (A Dell PowerEdge R640 with 2
Intel Xeon Silver 4216 CPUs, 192 GB RAM and 512GB SSD + 2TB HDD). To do so, I used
&lt;a href=&#34;https://github.com/shiftstack/dev-install/&#34;&gt;github.com/shiftstack/dev-install&lt;/a&gt;, an opinionated wrapper around &lt;a href=&#34;https://docs.openstack.org/project-deploy-guide/tripleo-docs/latest/deployment/standalone.html&#34;&gt;TripleO
Standalone&lt;/a&gt; designed for hosting OpenShift clusters, to deploy an OSP 16.2 cloud (OpenStack Train).
&lt;em&gt;dev-install&lt;/em&gt; is available on GitHub:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ git clone https://github.com/shiftstack/dev-install
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ cd dev-install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using &lt;em&gt;dev-install&lt;/em&gt; is relatively easy: all it needs is a &lt;code&gt;local_overrides.yaml&lt;/code&gt; file that contains information about
the IP address and hostname of the server, the OSP release you wish to use, and any other overrides necessary. I didn&amp;rsquo;t
need anything special so there were no overrides in my case. This meant my &lt;code&gt;local_overrides.yaml&lt;/code&gt; file looked something
like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;hostname&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;acme&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;public_api&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10.1.240.35&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;standalone_host&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;acme.shiftstack.test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;rhos_release&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;16.2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;clouddomain&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;shiftstack.test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the &lt;code&gt;local_overrides.yaml&lt;/code&gt; file in place, you can kick off installation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ make config host&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;acme.shiftstack.test
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ make osp_full overrides&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;local_overrides.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Installation should take about 30 minutes and two new entries, &lt;code&gt;standalone&lt;/code&gt; and &lt;code&gt;standalone_openshift&lt;/code&gt;, will be added to
your &lt;code&gt;clouds.yaml&lt;/code&gt; file upon completion.&lt;/p&gt;
&lt;p&gt;Once installation is completed, there is one final step necessary: starting &lt;code&gt;sshuttle&lt;/code&gt;. &lt;em&gt;dev-install&lt;/em&gt; creates a
&lt;code&gt;hostonly&lt;/code&gt; network that is not routable outside the host. As a result, you need something to proxy requests for the
associated &lt;code&gt;hostonly-subnet&lt;/code&gt; subnet to the host and &lt;em&gt;dev-install&lt;/em&gt; opts to use &lt;code&gt;sshuttle&lt;/code&gt; for this. You can start
&lt;code&gt;sshuttle&lt;/code&gt; using the wrapper script provided:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ ./scripts/sshuttle-standalone.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;bgp&#34;&gt;BGP&lt;/h2&gt;
&lt;p&gt;Next, we need to configure a BGP environment. I didn&amp;rsquo;t have access to some ToR switches with BGP support so we
opted to emulate it using &lt;a href=&#34;https://frrouting.org/&#34;&gt;&lt;code&gt;frr&lt;/code&gt;&lt;/a&gt;, a software routing solution. To do this, I used
&lt;a href=&#34;https://github.com/shiftstack/poc-bgp/&#34;&gt;github.com/shiftstack/poc-bgp&lt;/a&gt;, a set of Ansible playbooks which deployed four instances on my new OpenStack
cloud: a spine router instance and three leaf router instances. Once again, there are other software routing solutions
available and you might even have access to real hardware, so if you opt for another approach you could obviously skip
this section (though if you do, you will likely need to modify your MetalLB configuration when we get to that step in
the next post). Assuming you opt to use &lt;code&gt;poc-bgp&lt;/code&gt;, you&amp;rsquo;ll need a CentOS 9 Stream image to exist on the cloud as well as
a key pair so that you can SSH into the instances it creates. You can create the image like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ wget https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-20230417.0.x86_64.qcow2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack image create --public --disk-format qcow2 --file CentOS-Stream-GenericCloud-9-20230417.0.x86_64.qcow2 centos9-stream
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Similarly, you can create the key pair like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack keypair create --public-key ~/.ssh/id_ed25519.pub default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once these resources exists, configuration and deployment is otherwise relatively uncomplex.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;❯ git clone https://github.com/shiftstack/poc-bgp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;❯ cd poc-bgp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;❯ cat &amp;lt;&amp;lt; EOF &amp;gt; localvars.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;cloud_name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;standalone_openshift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;centos9-stream&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;instance_name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stephenfin-poc-bgp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;external_network&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;hostonly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;keypair&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;dns_nameservers&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1.1.1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;flavor&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;m1.tiny&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;❯ make deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once deployed you will end up with 4 instances and 7 networks (6 plus the &lt;code&gt;hostonly&lt;/code&gt; network):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack server list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------------------------+----------------------------------+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------+-----------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| ID                                   | Name                             | Status | Networks                                                                                                                                                                   | Image          | Flavor    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------------------------+----------------------------------+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------+-----------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| a3375961-0821-4135-b064-b884460d11c2 | stephenfin-poc-bgp-spine-gateway | ACTIVE | hostonly&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.25.191, 2001:db8::100; stephenfin-poc-bgp-rack1-patch&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.0.1; stephenfin-poc-bgp-rack2-patch&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.0.5; stephenfin-poc-bgp-rack3-patch&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.0.9 | centos9-stream | m1.tiny   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| f1e3b324-bf60-440e-808a-3e548f571f1a | stephenfin-poc-bgp-rack1-gateway | ACTIVE | stephenfin-poc-bgp-rack1-leaf&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.10.1; stephenfin-poc-bgp-rack1-patch&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.0.2                                                                                     | centos9-stream | m1.tiny   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 2b6051cb-f98a-4112-8096-c5c96c56f05a | stephenfin-poc-bgp-rack2-gateway | ACTIVE | stephenfin-poc-bgp-rack2-leaf&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.20.1; stephenfin-poc-bgp-rack2-patch&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.0.6                                                                                     | centos9-stream | m1.tiny   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 2d14cda5-377a-4383-b6e8-feb50455fad0 | stephenfin-poc-bgp-rack3-gateway | ACTIVE | stephenfin-poc-bgp-rack3-leaf&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.30.1; stephenfin-poc-bgp-rack3-patch&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;192.168.0.10                                                                                    | centos9-stream | m1.tiny   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------------------------+----------------------------------+--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------+-----------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack network list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------------------------+--------------------------------+----------------------------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| ID                                   | Name                           | Subnets                                                                    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------------------------+--------------------------------+----------------------------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 19708577-3757-4956-903e-9656229e1286 | stephenfin-poc-bgp-rack1-leaf  | 6152b856-781b-4fc9-9979-e4cfc9e282b8                                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 2a28f908-e9ec-4fb5-a4cb-09cd5dae8723 | stephenfin-poc-bgp-rack3-patch | 0f15d6ed-0b8a-435e-90d0-83edb2164100                                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 2cd308e3-8370-425b-af1c-0ce5562c2b36 | stephenfin-poc-bgp-rack1-patch | f22c9d7e-9c7f-4985-be7c-63eb05014544                                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 3060e6b0-c2c3-4f59-ac13-9d07a7a963f1 | stephenfin-poc-bgp-rack2-leaf  | 824cfd85-f89b-4a07-a330-3c3adb55b0e3                                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 538a2436-07c5-48f1-8cd0-39931244bcb7 | stephenfin-poc-bgp-rack3-leaf  | 3109bff8-fdd5-4824-ab2d-58184096e35c                                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 91efea92-eaf8-4146-ac4c-48d3edfedfe4 | hostonly                       | 607ba284-3762-4667-a2fa-2a7f31de6f35, b147f959-3122-4ad9-aaaa-6ff1af41c8df |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| b15da053-0625-4d33-ad6a-036dd76cfc8d | stephenfin-poc-bgp-rack2-patch | 71a6eeef-2c3d-45e2-80a9-efff6e15a289                                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------------------------------------+--------------------------------+----------------------------------------------------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;spine-gateway&lt;/code&gt; instance is connected to the three &lt;code&gt;rackN-gateway&lt;/code&gt; instances via separate patch networks, while the
three &lt;code&gt;rackN-gateway&lt;/code&gt; instances have their own &lt;code&gt;rackN-leaf&lt;/code&gt; leaf network. We&amp;rsquo;re going to use one of the latter as our
machine subnet when installing OpenShift shortly.&lt;/p&gt;
&lt;p&gt;We have some final steps to do here. First, we need to configure the subnets created by &lt;code&gt;poc-bgp&lt;/code&gt; to keep IP address
&amp;lt; &lt;code&gt;.10&lt;/code&gt; free. This is necessary because we are going to use some of these IP addresses for OpenShift VIPs shortly.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack subnet set --no-allocation-pool --allocation-pool &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;start=192.168.10.10,end=192.168.10.239&amp;#34;&lt;/span&gt; stephenfin-poc-bgp-rack1-subnet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack subnet set --no-allocation-pool --allocation-pool &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;start=192.168.20.10,end=192.168.20.239&amp;#34;&lt;/span&gt; stephenfin-poc-bgp-rack2-subnet
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack subnet set --no-allocation-pool --allocation-pool &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;start=192.168.30.10,end=192.168.30.239&amp;#34;&lt;/span&gt; stephenfin-poc-bgp-rack3-subnet
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In addition, we need another instance of &lt;code&gt;sshuttle&lt;/code&gt; to configure traffic for these new networks to route through the
&lt;code&gt;spine-gateway&lt;/code&gt; host. This will look something like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ sshuttle -r cloud-user@&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;GATEWAY_HOST_IP&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt; 192.168.10.0/24 192.168.20.0/24 192.168.30.0/24
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;where &lt;code&gt;${GATEWAY_HOST_IP}&lt;/code&gt; is the IP of the &lt;code&gt;spine-gateway&lt;/code&gt; host (&lt;code&gt;192.168.25.191&lt;/code&gt; in my case).&lt;/p&gt;
&lt;p&gt;With our BGP routers in place, we can move onto the final stage of set up: installing OpenShift.&lt;/p&gt;
&lt;h2 id=&#34;openshift&#34;&gt;OpenShift&lt;/h2&gt;
&lt;p&gt;The last step of initial set up is installing OpenShift. I used &lt;code&gt;openshift-install&lt;/code&gt; to do this, deploying a 4.12
OpenShift cluster on my OpenStack cloud. When deploying OpenShift, you&amp;rsquo;ll want to pay special attention to the
networking configuration. As noted above, we want to use one of the &lt;code&gt;rackN-leaf&lt;/code&gt; networks and I chose &lt;code&gt;rack1-leaf&lt;/code&gt;
arbitrarily. You should use IPs from whatever subnet you choose for your API and Ingress VIPs, picking address in the
&amp;lt; &lt;code&gt;.10&lt;/code&gt; range we previously set aside. This means you should end up with configuration similar to the following in
your &lt;code&gt;install-config.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;openstack&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;machinesSubnet&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;6152b856-781b-4fc9-9979-e4cfc9e282b8 &lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# stephenfin-poc-bgp-rack1-subnet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;apiVIPs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.10.5&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# from stephenfin-poc-bgp-rack1-subnet, &amp;lt; .10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ingressVIPs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.10.7&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# from stephenfin-poc-bgp-rack1-subnet, &amp;lt; .10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;networking&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;clusterNetworks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;cidr&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10.128.0.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/14&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;hostSubnetLength&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;serviceCIDR&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;172.30.0.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;machineNetwork&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;cidr&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.10.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/24&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;cidr&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.25.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/24&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In addition, assuming you don&amp;rsquo;t have a local DNS server configured, you will need to modify your &lt;code&gt;/etc/hosts&lt;/code&gt; file to
specify the hostnames and corresponding IP addresses of your OpenShift cluster. This is described in the
&lt;a href=&#34;https://github.com/openshift/installer/blob/master/docs/user/openstack/README.md&#34;&gt;OpenShift Installer docs&lt;/a&gt;, but in summary you&amp;rsquo;ll want something like this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# openshift shiftstack nodes
192.168.10.5 api.stephenfin.openshift.shiftstack.test
192.168.10.7 console-openshift-console.apps.stephenfin.openshift.shiftstack.test
192.168.10.7 integrated-oauth-server-openshift-authentication.apps.stephenfin.openshift.shiftstack.test
192.168.10.7 oauth-openshift.apps.stephenfin.openshift.shiftstack.test
192.168.10.7 prometheus-k8s-openshift-monitoring.apps.stephenfin.openshift.shiftstack.test
192.168.10.7 grafana-openshift-monitoring.apps.stephenfin.openshift.shiftstack.test
# End of openshift nodes
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should obviously change the hostnames and IP addresses to match whatever you configured in your
&lt;code&gt;install-config.yaml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Once you have these steps completed, you can kick of installation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openshift-install --log-level debug create cluster
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wait for that to complete, at which point you should have a fully functioning OpenShift deployment that you can interact
with using &lt;code&gt;oc&lt;/code&gt; or &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;wrap-up&#34;&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;In this post, we deployed an OpenStack deployment, configured BGP software routers, and deployed OpenShift on OpenStack.
In the next post, we will work to install MetalLB itself. Stay tuned.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;appendix-configuration-files&#34;&gt;Appendix: Configuration files&lt;/h2&gt;
&lt;p&gt;Here are the full configuration files used for my deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dev-install&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;local-overrides.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;hostname&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;acme&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;public_api&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10.1.240.35&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;standalone_host&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;acme.shiftstack.test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;rhos_release&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;16.2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;clouddomain&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;shiftstack.test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;poc-bgp&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;local_vars.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;cloud_name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;standalone_openshift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;centos9-stream&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;instance_name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stephenfin-poc-bgp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;external_network&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;hostonly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;keypair&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;dns_nameservers&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1.1.1.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;flavor&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;m1.tiny&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;openshift-installer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;install-config.yaml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;baseDomain&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;openshift.shiftstack.test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;stephenfin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;controlPlane&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;master&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;openstack&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;m1.xlarge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;replicas&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;compute&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;worker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;openstack&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;m1.xlarge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;replicas&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;platform&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;openstack&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;defaultMachinePlatform&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;m1.xlarge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;clusterOSImage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rhcos-4.12&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;cloud&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;standalone_openshift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;machinesSubnet&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;6152b856-781b-4fc9-9979-e4cfc9e282b8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;apiVIPs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.10.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;ingressVIPs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.10.7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;networking&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;clusterNetworks&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;cidr&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;10.128.0.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/14&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;hostSubnetLength&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;serviceCIDR&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;172.30.0.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;machineNetwork&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;cidr&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.10.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/24&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;cidr&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;192.168.25.0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;/24&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;pullSecret&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;&amp;lt;redacted&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Installing OSC With pipx</title>
      <link>https://that.guru/blog/installing-openstackclient-with-pipx/</link>
      <pubDate>Thu, 27 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/installing-openstackclient-with-pipx/</guid>
      <description>&lt;p&gt;Another one that really belongs in a separate &lt;em&gt;&amp;ldquo;tips &amp;amp; tricks&amp;rdquo;&lt;/em&gt; section of the blog, which unfortunately does not yet exist. I use &lt;a href=&#34;https://docs.openstack.org/python-openstackclient/latest/&#34;&gt;openstackclient (OSC)&lt;/a&gt;
pretty extensively day-to-day. Fedora provides a relatively up-to-date version of the package, but OSC has been evolving very rapidly of late and Fedora&amp;rsquo;s
packagers just can&amp;rsquo;t be that fast 😁. The solution, therefore, is to install from &lt;code&gt;pip&lt;/code&gt;. Installing things globally, even if that&amp;rsquo;s global to the user, is a bad
idea though: there are too many opportunities for dependency updates to break other packages. I want to use a virtualenv. The obvious solution is to use
&lt;a href=&#34;https://pypa.github.io/pipx/&#34;&gt;&lt;code&gt;pipx&lt;/code&gt;&lt;/a&gt;, which is specifically designed for this use case. From the &lt;a href=&#34;https://pypa.github.io/pipx/&#34;&gt;homepage&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;pipx is a tool to help you install and run end-user applications written in Python. It&amp;rsquo;s roughly similar to macOS&amp;rsquo;s &lt;code&gt;brew&lt;/code&gt;, JavaScript&amp;rsquo;s &lt;code&gt;npx&lt;/code&gt;, and Linux&amp;rsquo;s
&lt;code&gt;apt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s closely related to pip. In fact, it uses pip, but is focused on installing and managing Python packages that can be run from the command line directly as
applications.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;pipx&lt;/code&gt; itself is available in the Fedora repos so if Fedora is you distro of choice then you can install it with &lt;code&gt;dnf&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ sudo dnf install pipx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once that&amp;rsquo;s in place, you can install OSC with &lt;code&gt;pipx&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ pipx install python-openstackclient
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unlike &lt;code&gt;openstacksdk&lt;/code&gt;, however, the &lt;code&gt;python-openstackclient&lt;/code&gt; package doesn&amp;rsquo;t provide support for OpenStack services beyond the core ones: Identity (keystone),
Compute (nova), Image (glance), Block Storage (cinder), Network (neutron) and Object Storage (swift). For services like the Share Filesystem-as-a-Service
(manila), Load Balancer-as-a-Service (octavia) or Placement service, you need to install additional packages (&lt;code&gt;python-manilaclient&lt;/code&gt;, &lt;code&gt;python-octaviaclient&lt;/code&gt;, and
&lt;code&gt;osc-placement&lt;/code&gt;, respectively).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack loadbalancer list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;openstack: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;loadbalancer list&amp;#39;&lt;/span&gt; is not an openstack command. See &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;openstack --help&amp;#39;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Did you mean one of these?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  container create
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  container delete
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  container list
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  container save
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  container set
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  container show
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  container unset
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It took me a beat to figure out but &lt;code&gt;pipx&lt;/code&gt; provides an easy mechanism to do this: the &lt;code&gt;inject&lt;/code&gt; command. For example, to install &lt;code&gt;python-octaviaclient&lt;/code&gt; in the
same virtualenv, run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ pipx inject python-openstackclient python-octaviaclient
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can repeat this for additional dependencies:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ pipx inject python-openstackclient osc-placement python-neutronclient python-manilaclient python-ironicclient python-barbicanclient python-designateclient
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ pipx list --include-injected
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;venvs are in /home/stephenfin/.local/pipx/venvs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apps are exposed on your $PATH at /home/stephenfin/.local/bin
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   package python-openstackclient 6.2.0, installed using Python 3.11.2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - openstack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Injected Packages:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - osc-placement 4.1.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - python-barbicanclient 5.5.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - python-designateclient 5.2.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - python-ironicclient 5.1.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - python-manilaclient 4.3.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - python-neutronclient 9.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - python-octaviaclient 3.4.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With that done, you should have access to &lt;em&gt;all&lt;/em&gt; the commands. 💪&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Playing with OpenStack Nova APIs from a Python Shell</title>
      <link>https://that.guru/blog/interacting-with-nova-from-a-python-shell/</link>
      <pubDate>Wed, 22 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/interacting-with-nova-from-a-python-shell/</guid>
      <description>&lt;p&gt;A quick hack. If you want to call various OpenStack Nova APIs outside the context of a service like &lt;code&gt;nova-compute&lt;/code&gt;, there&amp;rsquo;s a good chance you&amp;rsquo;ll need some
configuration. If you have deployment, you&amp;rsquo;ll likely have a configuration file with all the necessary details. DevStack places its Nova configuration files at
&lt;code&gt;/etc/nova&lt;/code&gt;. Nova provides APIs, which are tiny wrappers around oslo.db functionality, for loading these.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; nova.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; nova &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parse_args(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;foo --config-file /etc/nova/nova.conf&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;split())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; CONF &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; nova&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;conf&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CONF
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With you configuration, you can now do things that require this configuration. For example, let&amp;rsquo;s look at our databases using SQLAlchemy.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from nova.db.main import api
&amp;gt;&amp;gt;&amp;gt; engine = api.get_engine()
&amp;gt;&amp;gt;&amp;gt; import sqlalchemy
&amp;gt;&amp;gt;&amp;gt; sqlalchemy.inspect(engine).has_table(&amp;#39;alembic_version&amp;#39;)
True
&amp;gt;&amp;gt;&amp;gt; sqlalchemy.inspect(engine).has_table(&amp;#39;migrate_version&amp;#39;)
False
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This can be handy to get you out of a hole, or to hack on stuff in a development environment.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Hello, Gophercloud</title>
      <link>https://that.guru/blog/gophercloud-hello-world/</link>
      <pubDate>Thu, 16 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/gophercloud-hello-world/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been playing around with &lt;a href=&#34;https://github.com/gophercloud/gophercloud&#34;&gt;Gophercloud&lt;/a&gt; over the past few days. While it&amp;rsquo;s nowhere near as complete as
&lt;a href=&#34;https://opendev.org/openstack/openstacksdk&#34;&gt;openstacksdk&lt;/a&gt; (which, coincidentally, recently hit the big &lt;a href=&#34;https://pypi.org/project/openstacksdk/1.0.0&#34;&gt;1.0.0 milestone&lt;/a&gt; 🎉) or
legacy Python-based service clients yet, it is clearly a great tool to have in your arsenal and is the library
underlying the &lt;a href=&#34;https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs&#34;&gt;Terraform OpenStack Provider&lt;/a&gt;, OpenStack platform support in
&lt;a href=&#34;https://github.com/openshift/installer&#34;&gt;openshift-install&lt;/a&gt;, and various OpenStack-specific Kubernetes components, among others.&lt;/p&gt;
&lt;p&gt;In the interests of easing ramp-up for anyone else trying to get started with &lt;em&gt;Gophercloud&lt;/em&gt;, here&amp;rsquo;s an simple &amp;ldquo;hello,
world&amp;rdquo; example - listing servers - using both &lt;em&gt;Gophercloud&lt;/em&gt; and &lt;em&gt;openstacksdk&lt;/em&gt;.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;Updated on 2025-06-11 to update examples for Gophercloud v2.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;gophercloud-go&#34;&gt;Gophercloud (Go)&lt;/h2&gt;
&lt;p&gt;Create a new directory containing a single file, &lt;code&gt;main.go&lt;/code&gt;, and add the following to it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gophercloud/utils/v2/openstack/clientconfig&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;opts&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; new(&lt;span style=&#34;color:#a6e22e&#34;&gt;clientconfig&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ClientOpts&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;// We could configure the cloud manually but we don&amp;#39;t. Instead we&amp;#39;ll leave it unset causing gophercloud to load the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;// cloud from the &amp;#39;OS_CLOUD&amp;#39; environment variable. We could also simplify this further and pass &amp;#39;nil&amp;#39; below instead&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;// of generating and passing a &amp;#39;ClientOpts&amp;#39; object but this shows how you _could_ configure things if you so chose.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;// opts.Cloud = &amp;#34;devstack-admin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clientconfig&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewServiceClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TODO&lt;/span&gt;(), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;compute&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;opts&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;pager&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;servers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;List&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;AllPages&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TODO&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;servers&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;servers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ExtractServers&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;pager&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;servers:&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;servers&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;  server %d: id=%s\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ID&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now generate the &lt;code&gt;go.mod&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ go mod init gophercloud-hello-world
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ go mod tidy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, you can run the command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ export OS_CLOUD&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;devstack-admin  &lt;span style=&#34;color:#75715e&#34;&gt;# or another entry from your local &amp;#39;clouds.yaml&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ go run .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should dump something like the following.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;servers:
  server 0: id=395b60b4-73be-4ba4-a8a8-fdaa3fce57d5
&lt;/code&gt;&lt;/pre&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;This example also makes use of the &lt;a href=&#34;https://github.com/gophercloud/utils&#34;&gt;gophercloud/utils&lt;/a&gt; library, to simplify authentication and the
creation of clients. &lt;em&gt;gophercloud&lt;/em&gt; itself also provides support for &lt;code&gt;clouds.yaml&lt;/code&gt; files, but its slightly more verbose.
If you wanted to skip &lt;code&gt;utils&lt;/code&gt;, you could rewrite this like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gophercloud/gophercloud/v2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gophercloud/gophercloud/v2/openstack&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gophercloud/gophercloud/v2/openstack/config&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gophercloud/gophercloud/v2/openstack/config/clouds&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TODO&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;ao&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;eo&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;tlsConfig&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clouds&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Parse&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;providerClient&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewProviderClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ao&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WithTLSConfig&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;tlsConfig&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;openstack&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NewComputeV2&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ctx&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;providerClient&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;eo&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;// rest of code here ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/aside&gt;


&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;If you are working on Gophercloud itself, it can be helpful to use scripts like this to test your changes.
If doing this, you can rely on the &lt;code&gt;replace&lt;/code&gt; directive in &lt;code&gt;go.mod&lt;/code&gt; to ensure your local clone of Gophercloud is used.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;github&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;com&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gophercloud&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gophercloud&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;v2&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;home&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gophercloud&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;github&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;com&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;gophercloud&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;utils&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;v2&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;home&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;utils&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;More information &lt;a href=&#34;https://thewebivore.com/using-replace-in-go-mod-to-point-to-your-local-module/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;openstacksdk-python&#34;&gt;openstacksdk (Python)&lt;/h2&gt;
&lt;p&gt;Create a new directory containing a single file, &lt;code&gt;main.py&lt;/code&gt;, and add the following to it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; openstack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# We could configure the cloud manually but we don&amp;#39;t. Instead we&amp;#39;ll leave it unset causing openstacksdk to load the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# cloud from the &amp;#39;OS_CLOUD&amp;#39; environment variable.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# conn = openstack.connect(cloud=&amp;#39;devstack-admin&amp;#39;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    conn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; openstack&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    servers &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;compute&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;servers()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;servers:&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; i, server &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; enumerate(servers):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;  server &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;i&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;: id=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;server&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now create a virtual environment and install the dependencies.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ virtualenv .venv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ source .venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ pip install openstacksdk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, you can run the command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ export OS_CLOUD&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;devstack-admin  &lt;span style=&#34;color:#75715e&#34;&gt;# or another entry from your local &amp;#39;clouds.yaml&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ python main.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should dump identical output to the Go-based script.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;servers:
  server 0: id=395b60b4-73be-4ba4-a8a8-fdaa3fce57d5
&lt;/code&gt;&lt;/pre&gt;</description>
    </item>
    
    <item>
      <title>API Versioning in Openstack</title>
      <link>https://that.guru/blog/api-versioning-in-openstack/</link>
      <pubDate>Fri, 03 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/api-versioning-in-openstack/</guid>
      <description>&lt;p&gt;The API versioning schemes in use in OpenStack vary between services and have evolved since the early days of OpenStack.
There are two types of API versioning to consider: the &lt;em&gt;major&lt;/em&gt; version and the &lt;em&gt;minor&lt;/em&gt; version. Today, most OpenStack
services have settled on a single &lt;em&gt;major&lt;/em&gt; API version and have chosen to evolve the API without bumping the &lt;em&gt;major&lt;/em&gt; API
version any further. There are three API &lt;em&gt;minor&lt;/em&gt; versioning schemes in common use.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This post is based on changes I recently made to the &lt;em&gt;python-openstackclient&lt;/em&gt; documentation, which you can find
&lt;a href=&#34;https://docs.openstack.org/python-openstackclient/latest/contributor/humaninterfaceguide#api-versioning&#34;&gt;here&lt;/a&gt;. I&amp;rsquo;m
duplicating it here in the hopes that it&amp;rsquo;ll be more discoverable.&lt;/div&gt;
&lt;/aside&gt;


&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;Updated on 2025-06-11 to better describe the API versioning policies of Glance and Keystone.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;per-release-versions&#34;&gt;Per-release versions&lt;/h2&gt;
&lt;p&gt;This is used by the Image service (glance) and DNS service (designate). All changes to the API during a given release
cycle are gathered into a single new API version. As such, the API version will increase at most once per release.
You can continue to request older versions.&lt;/p&gt;
&lt;p&gt;For example, consider the Image (glance) API.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Release&lt;/th&gt;
          &lt;th&gt;Supported 2.x API versions&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Grizzly&lt;/td&gt;
          &lt;td&gt;2.0 - 2.1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Havana&lt;/td&gt;
          &lt;td&gt;2.0 - 2.2&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Kilo&lt;/td&gt;
          &lt;td&gt;2.0 - 2.3&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&amp;hellip;&lt;/td&gt;
          &lt;td&gt;&amp;hellip;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In theory, this means the API is purely additive and the new version can be used as a signal that an API addition is
available. Glance takes this a step further and includes or excludes API versions depending on whether an optional
feature is enabled or not.&lt;/p&gt;
&lt;h2 id=&#34;single-version&#34;&gt;Single version&lt;/h2&gt;
&lt;p&gt;This is used by the Identity service (keystone). It is similar to the per-release versions but unlike those, only a
single API version is exposed. Like those, however, this version can be used as signal that a feature is available.
Once again, this API schema is really intended to be purely additive, since there&amp;rsquo;s no way to opt into old behavior.
Keystone, for example, has never removed APIs or made non-additive changes like changing the type of a field or removing
it.&lt;/p&gt;
&lt;h2 id=&#34;microversions&#34;&gt;Microversions&lt;/h2&gt;
&lt;p&gt;This is used by multiple services including the Compute service (nova), Block Storage service (cinder), and Shared
Filesystem service (manila). Each change to the API will result in a new API version. As such, the API version can
increase multiple times per release. You can continue to request older versions.&lt;/p&gt;
&lt;p&gt;For example, consider the Compute (nova) API.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Release&lt;/th&gt;
          &lt;th&gt;Supported 2.x API versions&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Kilo&lt;/td&gt;
          &lt;td&gt;2.1 - 2.3&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Liberty&lt;/td&gt;
          &lt;td&gt;2.1 - 2.12&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Mitaka&lt;/td&gt;
          &lt;td&gt;2.1 - 2.25&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&amp;hellip;&lt;/td&gt;
          &lt;td&gt;&amp;hellip;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Unlike the previous examples, this API versioning schema allows for practically any change, including changing the type
of a field, adding and removing new fields, and adding or removing new APIS. If you want the old behavior, you simply
request an older microversion.&lt;/p&gt;
&lt;h2 id=&#34;extensions&#34;&gt;Extensions&lt;/h2&gt;
&lt;p&gt;This is used by the Networking service (neutron). It&amp;rsquo;s a versioning scheme that doesn&amp;rsquo;t use API versions. Instead, it
exposes a list of available extensions. An extension can add, remove or modify features and vendor-specific
functionality to the API. This can include API resources/routes as well as new fields in API requests and responses. If
you want to depend on a feature added by an extension, you should check if the extension is present.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Zero-downtime Upgrades With Alembic and SQLAlchemy</title>
      <link>https://that.guru/blog/zero-downtime-upgrades-with-alembic-and-sqlalchemy/</link>
      <pubDate>Sun, 13 Nov 2022 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/zero-downtime-upgrades-with-alembic-and-sqlalchemy/</guid>
      <description>&lt;p&gt;This is the blog form of a talk I delivered at &lt;a href=&#34;https://python.ie/pycon-2022/schedule/&#34;&gt;PyCon Ireland 2022&lt;/a&gt;. You can
find the slides &lt;a href=&#34;https://that.guru/talks/zero-downtime-upgrades-with-alembic-and-sqlalchemy&#34;&gt;here&lt;/a&gt; and the source code
&lt;a href=&#34;https://github.com/stephenfin/alembic-expand-contract-demo&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One thing to note before we begin is that while I mention OpenStack services below, this article isn&amp;rsquo;t actually about
OpenStack 😉 We&amp;rsquo;re merely using it as a good example. The problems that OpenStack needs to address such as scaling out
services in larger deployments are common to most distributed services.&lt;/p&gt;
&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;In the beginning, there were only two OpenStack services: Swift, which was responsible for object-storage, and Nova, which handled everything else. Over time, the list of services has &lt;a href=&#34;https://en.wikipedia.org/wiki/OpenStack#Release_history&#34;&gt;grown enormously&lt;/a&gt; as Nova has shed some of its former responsibilities to other services and the amounts of things an OpenStack cloud can do has grown. However, due to many projects&amp;rsquo; shared heritage (where they were split out from or modeled on Nova) and general design decisions made in the early days of OpenStack, even the newest OpenStack services tend to have a couple of commonalities in their design. Two of these are relevant to this article. Firstly, many of the services are designed to be horizontally scalable and almost always use a message broker to achieve this. This message broker is typically RabbitMQ and it provides a way for multiple instances of a service to communicate with each other. Secondly, without fail, OpenStack services will use a traditional relational DB to store data. The database backend is usually MySQL (although PostgreSQL is also an option) while SQLAlchemy is used for them ORM.&lt;/p&gt;
&lt;p&gt;These two design decisions are relevant because they contain an inherent conflict. There can be many instances of a service (e.g. &lt;code&gt;nova-conductor&lt;/code&gt; or &lt;code&gt;nova-api&lt;/code&gt;) but they will all share a single database. If the schema of the database changes then each of these services will need to understand the new format. Historically in OpenStack, the way to do this was to closely couple the source code and database schema. An upgrade to a new version of OpenStack would necessitate taking all services down, upgrading the service code and the database schema, and then restarting the services. It works but it requires potentially large API downtime: the table lock that MySQL grabs while migrating the schema will block all reads and writes until the migration is complete, and for large installations where tables can have many millions of rows that migration can take minutes if not hours. This isn&amp;rsquo;t good enough for most production enterprise clouds, let alone specific applications like Telco where SLAs are something that the &lt;em&gt;government&lt;/em&gt; cares about. What we want is the ability to do rolling upgrades. Enter expand-contract.&lt;/p&gt;
&lt;h2 id=&#34;the-expand-migrate-contract-pattern&#34;&gt;The Expand-Migrate-Contract pattern&lt;/h2&gt;
&lt;p&gt;We can start exploring the expand-contract pattern by thinking about what happens to the data in an application during a migration.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data can be added. For example, adding a new &lt;code&gt;name&lt;/code&gt; field to a user account model.&lt;/li&gt;
&lt;li&gt;Data can change. For example, &lt;a href=&#34;https://shinesolutions.com/2018/01/08/falsehoods-programmers-believe-about-names-with-examples/&#34;&gt;combining separate existing &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt; fields into a single &lt;code&gt;name&lt;/code&gt; field&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Data can be deleted. For example, dropping the old &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt; fields.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The basic idea behind the expand-migrate-contract migration pattern is to split the schema migrations into two groups: additive changes, which will handle the added data, and contractive/destructive changes, which will handle the removed data. The changed data is an example of a data migration and either the application or another set of migrations handles this. If you&amp;rsquo;re a visual person, you can visualize this like so:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://that.guru/media/zero-downtime-upgrades-with-alembic-and-sqlalchemy-1.png&#34;
    alt=&#34;A minimal real-world example of expand-migrate-contract&#34;&gt;&lt;figcaption&gt;
      &lt;h4&gt;A visualization of expand-migrate-contract&lt;/h4&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;By applying the expand-contract migration, we can move from an upgrade pattern like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stop all services&lt;/li&gt;
&lt;li&gt;Upgrade the services and apply all database schema migrations.&lt;/li&gt;
&lt;li&gt;Wait X minutes/hours/days for the schema migration to complete.&lt;/li&gt;
&lt;li&gt;Restart all services&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Apply the &amp;ldquo;expand&amp;rdquo; database schema migration&lt;/li&gt;
&lt;li&gt;Upgrade the service instances one-by-one until all services have been upgraded&lt;/li&gt;
&lt;li&gt;(Optional) Apply database data migrations&lt;/li&gt;
&lt;li&gt;(At some future point) Apply the &amp;ldquo;contract&amp;rdquo; database schema migrations&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are often a couple of additional steps that your application will need to do, but the basic idea here is that by adapting the expand-migrate-contract pattern in your application, you can both allow your database schema to evolve while avoiding undesirable downtime.&lt;/p&gt;
&lt;p&gt;Now that we have all this background out of the way, where do Alembic and SQLAlchemy fit into this equation? I&amp;rsquo;m glad you asked!&lt;/p&gt;
&lt;h2 id=&#34;initial-steps&#34;&gt;Initial steps&lt;/h2&gt;
&lt;p&gt;So before we start talking about alternative database upgrade plans, we need to have a platform to build on. To this end, let&amp;rsquo;s create an initial project structure and populate it with some initial SQLAlchemy models. I&amp;rsquo;ve based this on a slightly modified version of the sample models found in the &lt;a href=&#34;https://docs.sqlalchemy.org/en/14/orm/quickstart.html#declare-models&#34;&gt;SQLAlchemy quick start guide&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ mkdir -p alembic-expand-contract-demo/alembic_expand_contract_demo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ cd alembic-expand-contract-demo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ touch alembic_expand_contract_demo/__init__.py alembic_expand_contract_demo/models.py alembic_expand_contract_demo/README.md
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dump the following into the newly created &lt;code&gt;alembic_expand_contract_demo/models.py&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Column
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ForeignKey
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Integer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; String
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy.orm &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; declarative_base
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy.orm &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; relationship
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Base &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; declarative_base()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;User&lt;/span&gt;(Base):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user_account&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Column(Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    first_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Column(String(&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Column(String(&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addresses &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Address&amp;#34;&lt;/span&gt;, back_populates&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;, cascade&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;all, delete-orphan&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__repr__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User(id=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id&lt;span style=&#34;color:#e6db74&#34;&gt;!r}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Address&lt;/span&gt;(Base):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;address&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Column(Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email_address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Column(String, nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Column(Integer, ForeignKey(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user_account.id&amp;#34;&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; relationship(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User&amp;#34;&lt;/span&gt;, back_populates&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;addresses&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__repr__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Address(id=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id&lt;span style=&#34;color:#e6db74&#34;&gt;!r}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ll also create our &lt;code&gt;requirements.txt&lt;/code&gt; file and install those requirements.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ cat &amp;gt; requirements.txt &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;&amp;lt; EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;sqlalchemy
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;alembic
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;.venv&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; virtualenv .venv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;.venv&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; source .venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;.venv&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With our initial models in place, we can now proceed to creating the initial migration directory structure.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;.venv&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; alembic init alembic_expand_contract_demo/migrations
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should now have a directory structure like so:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;.
├── alembic_expand_contract_demo
│   ├── __init__.py
│   ├── migrations
│   │   ├── env.py
│   │   ├── README
│   │   ├── script.py.mako
│   │   └── versions
│   ├── models.py
├── alembic.ini
├── README.md
└── requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As suggested by the wizard, you&amp;rsquo;ll want to make a few changes to the &lt;code&gt;alembic.ini&lt;/code&gt; file created in the root of your directory. In particular, you&amp;rsquo;ll want to configure the &lt;code&gt;[alembic] script_location&lt;/code&gt; and &lt;code&gt;[alembic] sqlalchemy.url&lt;/code&gt; settings. I used the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[alembic]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;script_location&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;%(here)s/alembic_expand_contract_demo/migrations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;sqlalchemy.url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;sqlite:///alembic-expand-contract-demo.db&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll also need to configure the &lt;code&gt;env.py&lt;/code&gt; file found in the new &lt;code&gt;alembic_expand_contract_demo/migrations&lt;/code&gt; directory to point the &lt;code&gt;target_metadata&lt;/code&gt; value to your base model. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic_expand_contract_demo &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; models
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;target_metadata &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; models&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Base&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;metadata
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, we&amp;rsquo;ll modify the migration script template &lt;code&gt;script.py.mako&lt;/code&gt; in the &lt;code&gt;alembic_expand_contract_demo/migrations&lt;/code&gt; directory to remove the &lt;code&gt;downgrade&lt;/code&gt; step: downgrades aren&amp;rsquo;t all that useful in a production environment (back up your data!) and add needless complexity here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--- alembic_expand_contract_demo/migrations/script.py.mako
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+++ alembic_expand_contract_demo/migrations/script.py.mako
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -2,9 +2,8 @@
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Revision ID: ${up_revision}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Revises: ${down_revision | comma,n}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-Create Date: ${create_date}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt; &amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt; from alembic import op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; import sqlalchemy as sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ${imports if imports else &amp;#34;&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -18,7 +17,3 @@ depends_on = ${repr(depends_on)}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; def upgrade() -&amp;gt; None:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     ${upgrades if upgrades else &amp;#34;pass&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-def downgrade() -&amp;gt; None:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-    ${downgrades if downgrades else &amp;#34;pass&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With all this in place, we can now create an initial empty database and generate our first migration (Alembic needs a database to compare against to generate the updated schema).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic upgrade head
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once this is run, you&amp;rsquo;ll note a new &lt;code&gt;alembic-expand-contract-demo.db&lt;/code&gt; SQLite database in your root directory. A quick look at the schema of this database shows an effectively empty table with only Alembic&amp;rsquo;s own &lt;code&gt;alembic_version&lt;/code&gt; table present. This is expected: while we have models in place, we don&amp;rsquo;t yet have a Alembic migration to add them.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ sqlite3 alembic-expand-contract-demo.db &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.dump&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PRAGMA foreign_keys&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;OFF;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BEGIN TRANSACTION;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE TABLE alembic_version &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        version_num VARCHAR&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;32&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; NOT NULL,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        CONSTRAINT alembic_version_pkc PRIMARY KEY &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;version_num&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;COMMIT;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since we&amp;rsquo;d already wired everything up, we can now simply run the &lt;code&gt;revision&lt;/code&gt; sub-command with the &lt;code&gt;--autogenerate&lt;/code&gt; option and our migrations will be auto-generated:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic revision --autogenerate --message&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Initial models&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO  &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;alembic.runtime.migration&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Context impl SQLiteImpl.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO  &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;alembic.runtime.migration&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Will assume non-transactional DDL.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO  &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;alembic.autogenerate.compare&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Detected added table &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INFO  &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;alembic.autogenerate.compare&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt; Detected added table &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Generating /home/stephenfin/demos/alembic-expand-contract-demo/alembic_expand_contract_demo/migrations/versions/a185a7b16d70_initial_models.py ...  &lt;span style=&#34;color:#66d9ef&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This generates a migration that will look something like the following, once comments are addressed and formatting cleaned up:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Initial models
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revision ID: 6cb93d555e2b
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revises:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# revision identifiers, used by Alembic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6cb93d555e2b&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;down_revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;branch_labels &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;depends_on &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;upgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_table(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PrimaryKeyConstraint(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_table(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;email_address&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ForeignKeyConstraint(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account.id&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PrimaryKeyConstraint(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;downgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_table(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;address&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_table(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Apply this and we should see the new tables in the database schema.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic upgrade head
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ sqlite3 alembic-expand-contract-demo.db &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;.dump&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PRAGMA foreign_keys&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;OFF;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BEGIN TRANSACTION;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE TABLE alembic_version &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        version_num VARCHAR&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;32&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; NOT NULL,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        CONSTRAINT alembic_version_pkc PRIMARY KEY &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;version_num&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;INSERT INTO alembic_version VALUES&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6cb93d555e2b&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE TABLE user_account &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        id INTEGER NOT NULL,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        first_name VARCHAR&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;30&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        last_name VARCHAR&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;30&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        PRIMARY KEY &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;id&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE TABLE address &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        id INTEGER NOT NULL,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        email_address VARCHAR NOT NULL,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        user_id INTEGER NOT NULL,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        PRIMARY KEY &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;id&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FOREIGN KEY&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;user_id&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; REFERENCES user_account &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;id&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;COMMIT;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nothing complicated so far. Onto the meaty part.&lt;/p&gt;
&lt;h2 id=&#34;first-pass-at-expand-contract-using-alembic&#34;&gt;First pass at expand-contract using Alembic&lt;/h2&gt;
&lt;p&gt;We now have an initial application. Time to start thinking about how to upgrade it. Looking at our models, we&amp;rsquo;ll note that we have separate &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt; fields.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;User&lt;/span&gt;(Base):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user_account&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Column(Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    first_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Column(String(&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Column(String(&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addresses &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Address&amp;#34;&lt;/span&gt;, back_populates&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;, cascade&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;all, delete-orphan&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__repr__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;User(id=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id&lt;span style=&#34;color:#e6db74&#34;&gt;!r}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s say we wanted to merge these two fields into a single combined field called &lt;code&gt;name&lt;/code&gt;. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -17,8 +17,7 @@ Base = declarative_base()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; class User(Base):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     __tablename__ = &amp;#34;user_account&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     id = Column(Integer, primary_key=True)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-    first_name = Column(String(30))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-    last_name = Column(String(30))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+    name = Column(String)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;     addresses = relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &amp;#34;Address&amp;#34;, back_populates=&amp;#34;user&amp;#34;, cascade=&amp;#34;all, delete-orphan&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can auto-generate our migration once again:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic revision --message&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Merge user names&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will give us a migration like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Merge user names
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revision ID: 0585005489f0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revises: 6cb93d555e2b
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# revision identifiers, used by Alembic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0585005489f0&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;down_revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6cb93d555e2b&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;branch_labels &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;depends_on &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;upgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_column(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;downgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_column(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;VARCHAR(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_column(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;VARCHAR(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Clearly this isn&amp;rsquo;t very useful as it would result in data loss: we need to migrate data before we can remove the old column. The easy way to address this would be to modify the migration to also migrate data as an intermediary step. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Merge user names
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revision ID: 0585005489f0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revises: 6cb93d555e2b
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# revision identifiers, used by Alembic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0585005489f0&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;down_revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6cb93d555e2b&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;branch_labels &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;depends_on &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;upgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# schema migrations - expand&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_column(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# data migrations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bind &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_bind()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_account_table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Table(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;MetaData(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String, nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    names &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; bind&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;select(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;first_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;last_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fetchall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; id, first_name, last_name &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; names:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bind&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;update()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;where(user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;values(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;first_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;last_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# schema migrations - contract&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;downgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exception&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Irreversible migration&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;We&amp;rsquo;re using Python to do our data migrations here so this migration would not be able to run in Alembic&amp;rsquo;s &amp;ldquo;offline&amp;rdquo; mode. You&amp;rsquo;d need to rewrite this to use SQL if you wanted this functionality.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;While this works, as noted at the outset this approach doesn&amp;rsquo;t work for scaled applications or applications with uptime guarantees: your applications would need to be taken offline while this database schema was applied, which itself is an operation that could take some time. Let&amp;rsquo;s try an expand-contract approach instead. First, we&amp;rsquo;ll do this manually. Instead of having one large migration file, we&amp;rsquo;ll create three separate migration files (or two, if your application is going to take care of data migrations at runtime).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic revision --message&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Merge user names (expand)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic revision --message&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Merge user names (migrate)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic revision --message&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Merge user names (contract)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the &amp;ldquo;expand&amp;rdquo; migration, we add the new column:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Merge user names (expand)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revision ID: 9c36df1b3f62
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revises: 6cb93d555e2b
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# revision identifiers, used by Alembic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;9c36df1b3f62&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;down_revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;6cb93d555e2b&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;branch_labels &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;depends_on &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;upgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_column(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the &amp;ldquo;migrate&amp;rdquo; migration, we migrate our data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Merge user names (migrate)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revision ID: c9afc30a70ee
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revises: 9c36df1b3f62
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# revision identifiers, used by Alembic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;c9afc30a70ee&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;down_revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;9c36df1b3f62&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;branch_labels &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;depends_on &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;upgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# data migrations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bind &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_bind()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_account_table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Table(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;MetaData(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String, nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    names &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; bind&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;select(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;first_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;last_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fetchall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; id, first_name, last_name &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; names:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bind&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;update()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;where(user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;values(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;first_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;last_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, in the &amp;ldquo;contract&amp;rdquo; migration, we remove the old columns:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Merge user names (contract)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revision ID: e629a4ea0677
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revises: c9afc30a70ee
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# revision identifiers, used by Alembic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;e629a4ea0677&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;down_revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;c9afc30a70ee&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;branch_labels &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;depends_on &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;upgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# schema migrations - contract&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is better. However, it does mean you can&amp;rsquo;t simply run &lt;code&gt;alembic upgrade head&lt;/code&gt; as this would apply all migrations in one go, totally defeating the point. What we want is a way to only apply the &lt;em&gt;expand&lt;/em&gt; migrations initially, waiting to apply the other migrations later once all our services have been updated. This leads us to&amp;hellip;&lt;/p&gt;
&lt;h2 id=&#34;branches&#34;&gt;Branches&lt;/h2&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;From here on, we&amp;rsquo;re going to take the &lt;em&gt;migrate&lt;/em&gt; phase out of Alembic&amp;rsquo;s hand and do this manually. This would better represent a world when the application(s) would do this work. It also simplifies our examples. However, there&amp;rsquo;s no reason you couldn&amp;rsquo;t have three phases instead of two in your own application.&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;Per the &lt;a href=&#34;https://alembic.sqlalchemy.org/en/latest/branches.html&#34;&gt;Alembic docs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A branch describes a point in a migration stream when two or more versions refer to the same parent migration as their ancestor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They&amp;rsquo;re intended for scenarios where &amp;ldquo;two divergent source trees, both containing Alembic revision files created independently within those source trees, are merged together into one&amp;rdquo;. This could includes things like feature branches, where multiple developers are working on separate features and merge everything back in around the same time. However, we can also use them to separate our &lt;em&gt;expand&lt;/em&gt; migrations from our &lt;em&gt;contract&lt;/em&gt; migrations. Let&amp;rsquo;s look at how we&amp;rsquo;d do that. First the &lt;em&gt;expand&lt;/em&gt; migration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--- alembic_expand_contract_demo/migrations/versions/9c36df1b3f62_merge_user_names_expand.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+++ alembic_expand_contract_demo/migrations/versions/9c36df1b3f62_merge_user_names_expand.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -11,7 +11,7 @@ import sqlalchemy as sa
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; # revision identifiers, used by Alembic.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; revision = &amp;#39;9c36df1b3f62&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; down_revision = &amp;#39;6cb93d555e2b&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-branch_labels = None
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+branch_labels = (&amp;#39;expand&amp;#39;,)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt; depends_on = None
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All that we&amp;rsquo;ve done here is added a new &lt;code&gt;expand&lt;/code&gt; branch label. Nothing odd there. Next up, the &lt;em&gt;expand&lt;/em&gt; migration. As noted above, we&amp;rsquo;re actually going to skip this going forward since it&amp;rsquo;s not totally relevant, so let&amp;rsquo;s delete it and create a separate tool to manage the migrations.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ rm alembic_expand_contract_demo/migrations/versions/c9afc30a70ee_merge_user_names_migrate.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ touch data-migration.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;migrate_data&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    engine &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_engine(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sqlite:///alembic-expand-contract-demo.db&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_account_table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Table(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;MetaData(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(length&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String, nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; engine&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;connect() &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; conn:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        names &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;select(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;first_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;last_name,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fetchall()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; id, first_name, last_name &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; names:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            conn&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;execute(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;update()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;where(user_account_table&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;c&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;values(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;first_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;last_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    migrate_data()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, the &lt;em&gt;contract&lt;/em&gt; migration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--- alembic_expand_contract_demo/migrations/versions/e629a4ea0677_merge_user_names_contract.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+++ alembic_expand_contract_demo/migrations/versions/e629a4ea0677_merge_user_names_contract.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -9,8 +9,8 @@ from alembic import op
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; # revision identifiers, used by Alembic.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; revision = &amp;#39;e629a4ea0677&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-down_revision = &amp;#39;c9afc30a70ee&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-branch_labels = None
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+down_revision = &amp;#39;9c36df1b3f62&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+branch_labels = (&amp;#39;contract&amp;#39;,)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt; depends_on = None
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, nothing too odd here. We add the &lt;code&gt;contract&lt;/code&gt; branch label and change the down revision to reflect the fact that we removed the data migration.&lt;/p&gt;
&lt;p&gt;With these changes in place, we now have the ability to apply the branches separately.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic upgrade expand
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ python data-migration.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic upgrade contract
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;If you wanted to add additional migrations to either branch, you can use the &lt;code&gt;--branch-label&lt;/code&gt; and &lt;code&gt;--depends-on&lt;/code&gt; options to set the branch and dependency migration, respectively. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic revision &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --branch-label expand &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --depends-on 9c36df1b3f62 &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;    --message &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Another expand type change&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;This is a further improvement from where we were, but we&amp;rsquo;re still missing a huge feature: auto-generation. While we can pass the &lt;code&gt;--autogenerate&lt;/code&gt; flag &lt;code&gt;alembic revision&lt;/code&gt;, the generated migrations need a lot of work including manually splitting the operations into expand and contract branch migrations. It would be great if Alembic could do this for us. Turns out it can, but it needs a bit of work.&lt;/p&gt;
&lt;h2 id=&#34;extending-auto-generation-to-support-expand-contract&#34;&gt;Extending auto-generation to support expand-contract&lt;/h2&gt;
&lt;p&gt;What we need is a hook point to modify the migrations generated by Alembic so that we can separate them into &lt;em&gt;expand&lt;/em&gt; migrations and &lt;em&gt;contract&lt;/em&gt; migrations. To do this, Alembic provides &lt;code&gt;process_revision_directives&lt;/code&gt; attribute to the &lt;code&gt;context.configure&lt;/code&gt; API call found in &lt;code&gt;env.py&lt;/code&gt;. From the &lt;a href=&#34;https://alembic.sqlalchemy.org/en/latest/api/runtime.html#alembic.runtime.environment.EnvironmentContext.configure.params.process_revision_directives&#34;&gt;Alembic docs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;process_revision_directives&lt;/strong&gt; - a callable function that will be passed a structure representing the end result of an autogenerate or plain “revision” operation, which can be manipulated to affect how the alembic revision command ultimately outputs new revision scripts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before we configure that attribute, let&amp;rsquo;s create the callable function itself. To do this, we&amp;rsquo;re going to make use of the &lt;code&gt;alembic.util.Dispatcher&lt;/code&gt; class. This is a class that is normally responsible for generating per-backend SQL statements for each migration operation. Instead of using this to generate SQL statements, we can use it to sift through the operations generated by Alembic so we can split them into two phases. Let&amp;rsquo;s look at how we can do this in practice. Create an new file, &lt;code&gt;alembic_expand_contract_demo/migrations/autogen.py&lt;/code&gt; and dump the following into it. We&amp;rsquo;ll discuss the contents of the file step-by-step in a bit.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# SPDX-License-Identifier: MIT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic.operations &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; ops
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic.util &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Dispatcher
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic.util &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; rev_id &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; new_rev_id
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_ec_dispatcher &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Dispatcher()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_revision_directives&lt;/span&gt;(context, revision, directives):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    directives[:] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(_assign_directives(context, directives))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_assign_directives&lt;/span&gt;(context, directives, phase&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; directive &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; directives:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        decider &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _ec_dispatcher&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dispatch(directive)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            phases &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;expand&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contract&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            phases &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (phase,)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; phases:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            decided &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; decider(context, directive, phase)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; decided:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; decided
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;MigrationScript)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_migration_script_ops&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;MigrationScript(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        new_rev_id(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;UpgradeOps(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ops&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;list(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                _assign_directives(context, directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;upgrade_ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ops, phase)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DowngradeOps(ops&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[]),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        message&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;message,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        head&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;phase&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;@head&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;upgrade_ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;is_empty():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ModifyTableOps)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_modify_table_ops&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ModifyTableOps(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;table_name, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ops&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;list(_assign_directives(context, directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ops, phase)),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        schema&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;schema,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;is_empty():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;AddConstraintOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CreateIndexOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CreateTableOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;AddColumnOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_expands&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;expand&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DropConstraintOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DropIndexOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DropTableOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DropColumnOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_contracts&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contract&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;AlterColumnOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_alter_column&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    is_expand &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;expand&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; is_expand &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;modify_nullable &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; is_expand &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;modify_nullable &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NotImplementedError&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Don&amp;#39;t know if operation is an expand or contract at the moment: &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With that file in place, let&amp;rsquo;s take a look at what it&amp;rsquo;s doing. First, the &lt;code&gt;process_revision_directives&lt;/code&gt; and &lt;code&gt;_assign_directives&lt;/code&gt; functions:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;_ec_dispatcher &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Dispatcher()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process_revision_directives&lt;/span&gt;(context, revision, directives):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    directives[:] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; list(_assign_directives(context, directives))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_assign_directives&lt;/span&gt;(context, directives, phase&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; directive &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; directives:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        decider &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _ec_dispatcher&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dispatch(directive)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            phases &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;expand&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contract&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            phases &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (phase,)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; phases:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            decided &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; decider(context, directive, phase)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; decided:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;yield&lt;/span&gt; decided
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hopefully this should be mostly self-explanatory. &lt;code&gt;process_revision_directives&lt;/code&gt; is initial &amp;ldquo;entry point&amp;rdquo; and all it does is overwrites the provided list of directives with our modified set. &lt;code&gt;_assign_directives&lt;/code&gt;, on the other hand, does the bulk heavy lifting. It&amp;rsquo;s responsible for iterating through the generated directives calling the dispatcher for each phase that we&amp;rsquo;re examining.&lt;/p&gt;
&lt;p&gt;Next, we look to look at the dispatchers themselves. For each type of operation, we need to write a separate dispatcher. The top-level operation is the generation of the migration script itself a.k.a. the &lt;a href=&#34;https://alembic.sqlalchemy.org/en/latest/api/operations.html#alembic.operations.ops.MigrationScript&#34;&gt;&lt;code&gt;MigrationScript&lt;/code&gt; operation&lt;/a&gt;. Let&amp;rsquo;s look at this first.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;MigrationScript)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_migration_script_ops&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;MigrationScript(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        new_rev_id(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;UpgradeOps(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ops&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;list(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                _assign_directives(context, directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;upgrade_ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ops, phase)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DowngradeOps(ops&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[]),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        message&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;message,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        head&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;phase&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;@head&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;upgrade_ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;is_empty():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, hopefully there&amp;rsquo;s nothing too complicated. This dispatcher generates a migration script container a filtered list of upgrade directives (and no downgrade directives). This filtered list is filtered by phase, and we also specify a &lt;em&gt;head&lt;/em&gt; that corresponds to the phase (&lt;code&gt;expand@head&lt;/code&gt; for the &lt;em&gt;expand&lt;/em&gt; phase).&lt;/p&gt;
&lt;p&gt;Moving on, in addition to the &lt;code&gt;MigrationScript&lt;/code&gt; operation, we have one other &amp;ldquo;meta&amp;rdquo; operation to concern ourselves with: &lt;code&gt;ModifyTableOps&lt;/code&gt;. Fortunately, this isn&amp;rsquo;t too complicated. We basically ensure that all operations against a table are filtered by phase as we&amp;rsquo;d expect:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ModifyTableOps)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_modify_table_ops&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ModifyTableOps(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;table_name, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ops&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;list(_assign_directives(context, directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ops, phase)),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        schema&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;schema,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;is_empty():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now for the first &amp;ldquo;real&amp;rdquo; operations: the &lt;em&gt;expand&lt;/em&gt; operations. These are &lt;code&gt;AddConstraintOp&lt;/code&gt;, &lt;code&gt;CreateIndexOp&lt;/code&gt;, &lt;code&gt;CreateTableOp&lt;/code&gt;, and &lt;code&gt;AddColumnOp&lt;/code&gt;. Let&amp;rsquo;s look at the dispatcher for these.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;AddConstraintOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CreateIndexOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CreateTableOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;AddColumnOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_expands&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;expand&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Simply put, if we&amp;rsquo;re in &lt;em&gt;expand&lt;/em&gt; phase and encounter one of these operations, proceed as normal. If we&amp;rsquo;re in the &lt;em&gt;contract&lt;/em&gt; phase, skip it. As you&amp;rsquo;d imagine, the dispatcher for the &lt;em&gt;contract&lt;/em&gt; options - &lt;code&gt;DropConstraintOp&lt;/code&gt;, &lt;code&gt;DropIndexOp&lt;/code&gt;, &lt;code&gt;DropTableOp&lt;/code&gt;, and &lt;code&gt;DropColumnOp&lt;/code&gt; - works pretty similarly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DropConstraintOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DropIndexOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DropTableOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;DropColumnOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_contracts&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;contract&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the plain old create and destroy operations out of the way, we only have to take care of the remaining &amp;ldquo;modify&amp;rdquo; or &amp;ldquo;alter&amp;rdquo; operations. This consists of modifications to columns a.k.a. &lt;code&gt;AlterColumnOp&lt;/code&gt;. The &lt;code&gt;AlterColumnOp&lt;/code&gt; dispatcher is trickier that the others, so pay attention here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@_ec_dispatcher.dispatch_for&lt;/span&gt;(ops&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;AlterColumnOp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_alter_column&lt;/span&gt;(context, directive, phase):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    is_expand &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; phase &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;expand&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; is_expand &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;modify_nullable &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;elif&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; is_expand &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; directive&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;modify_nullable &lt;span style=&#34;color:#f92672&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NotImplementedError&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Don&amp;#39;t know if operation is an expand or contract at the moment: &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; directive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The only modification we support here is the modifying &lt;code&gt;NULL&lt;/code&gt; constraints on columns. There are other modifications you can make, such as modifying the type or a server default but these operations should really involve a bit of manual intervention to handle correctly. What we&amp;rsquo;ve said here is that if we&amp;rsquo;re in the &lt;em&gt;expand&lt;/em&gt; phase then settings a column to nullable is a-okay. Likewise, if we&amp;rsquo;re in the &lt;em&gt;contract&lt;/em&gt; phase then setting a column to non-nullable is also permissible. All other cases are forbidden though and will raise a &lt;code&gt;NotImplementedError&lt;/code&gt; exception.&lt;/p&gt;
&lt;p&gt;With this in place, the final step is to enable the processor by passing the &lt;code&gt;process_revision_directives&lt;/code&gt; argument to the &lt;code&gt;context.configure&lt;/code&gt; API call in &lt;code&gt;env.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--- alembic_expand_contract_demo/migrations/env.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+++ alembic_expand_contract_demo/migrations/env.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -5,6 +5,7 @@ from sqlalchemy import engine_from_config
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; from sqlalchemy import pool
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; from alembic_expand_contract_demo import models
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+from alembic_expand_contract_demo.migrations import autogen
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; # this is the Alembic Config object, which provides
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; # access to the values within the .ini file in use.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -68,6 +69,7 @@ def run_migrations_online() -&amp;gt; None:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;         context.configure(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             connection=connection,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             target_metadata=target_metadata,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+            process_revision_directives=autogen.process_revision_directives,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;         )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         with context.begin_transaction():
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;testing-things-out&#34;&gt;Testing things out&lt;/h2&gt;
&lt;p&gt;Now that we have all of our machinery in place, it&amp;rsquo;s time to take things for a spin. Let&amp;rsquo;s test out auto-generation using similar commands to those we used as the beginning of the article.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic revision --autogenerate --message&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Merge user names&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first thing we notice is that there are no longer one but &lt;strong&gt;two&lt;/strong&gt; generations generated. This looks promising and looking at the migrations themselves things look even better. First the &lt;em&gt;expand&lt;/em&gt; migration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ cat alembic_expand_contract_demo/migrations/versions/1630422c66fe_merge_user_names.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Merge user names
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revision ID: 1630422c66fe
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revises: 9c36df1b3f62
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# revision identifiers, used by Alembic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;1630422c66fe&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;down_revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;9c36df1b3f62&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;branch_labels &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;depends_on &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;upgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_column(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the &lt;em&gt;contract&lt;/em&gt; migration:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ cat alembic_expand_contract_demo/migrations/versions/a142d030afc5_merge_user_names.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Merge user names
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revision ID: a142d030afc5
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Revises: e629a4ea0677
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; alembic &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; op
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# revision identifiers, used by Alembic.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;a142d030afc5&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;down_revision &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;e629a4ea0677&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;branch_labels &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;depends_on &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;upgrade&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;last_name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    op&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;drop_column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_account&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;first_name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Applying these works exactly as expected:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic upgrade expand
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ alembic upgrade contract
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hurrah, everything worked (hopefully)! However, this is only the first step. While this is a marked improvement on where we were, there are a few improvements we could yet make. For one, the names of the files don&amp;rsquo;t reveal information about whether a change is an &lt;em&gt;expand&lt;/em&gt; or &lt;em&gt;contract&lt;/em&gt; migration. You can figure this out by looking at the migrations themselves but it would be much nicer if we could somehow indicate this through the filenames or, better yet, by separating them into separate folders. The branches here are also long-running and it would be nice if we could apply, say, all expand migrations up to a specific release along with any contract migrations from the previous release (which should be okay to apply assuming you&amp;rsquo;ve upgraded all services to the previous release version and have been running it for some time to migrate data). Both of these requests (and more!) can be achieved, but that&amp;rsquo;s outside the scope of this article. I&amp;rsquo;d highly recommend looking at the source code of the OpenStack Neutron project, which implements this functionality in a full-featured manner.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Quotas in OpenStack</title>
      <link>https://that.guru/blog/quotas-in-openstack/</link>
      <pubDate>Fri, 07 Oct 2022 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/quotas-in-openstack/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been playing around with quotas in OpenStack again. Every time I do, I
encounter another strange bit of behavior that catches me out. This time, I&amp;rsquo;ve
decided to write down these strange things so at least I have a reference to go
back to at some point in the future. I should probably get these notes into the
docs for nova, cinder and neutron at some point&amp;hellip;&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;This post focuses on the nova (compute), cinder (block storage), and neutron
(networking) services. There are other OpenStack services that implement quotas
but I&amp;rsquo;m not overly familiar with their quota models. However, I would suspect
that what&amp;rsquo;s discussed here &lt;em&gt;should&lt;/em&gt; be broadly applicable.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;project-quotas-and-default-quotas&#34;&gt;Project quotas and default quotas&lt;/h2&gt;
&lt;p&gt;Quotas are a complicated area with a lot of baggage. Broadly speaking, there
are two types of quota: default quotas and project-specific quotas. The default
quotas are applied to projects unless specifically overridden by project
quotas. In addition, for nova and cinder, there are two types of default quota:
API-configured default quotas and statically configured default quotas defined
in config files (so &lt;code&gt;nova.conf&lt;/code&gt; or &lt;code&gt;cinder.conf&lt;/code&gt; respectively). Neutron only
supports statically configured quotas (&lt;code&gt;neutron.conf&lt;/code&gt;). Project specific quotas
take precedence over API-configured default quotas (where available), which in
turn take priority over statically configured default quotas. We can visualize
this as such:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;nova:    conf default quotas &amp;lt; API default quotas &amp;lt; project quotas
cinder:  conf default quotas &amp;lt; API default quotas &amp;lt; project quotas
neutron: conf default quotas           &amp;lt;            project quotas
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;user-specific-quotas&#34;&gt;User-specific quotas&lt;/h2&gt;
&lt;p&gt;Nova has another type of quota: user-specific quotas. There&amp;rsquo;s only one type of
user-specific quota in nova - &lt;code&gt;keypairs&lt;/code&gt; and they&amp;rsquo;re not something that nova is
likely to continue supporting in the long-term. As you&amp;rsquo;d expect, the primary
difference between between user-specific quotas and project-specific quotas is
that user-specific quotas are tied to the user instead of the project. This
means you can specify that user &lt;code&gt;foo&lt;/code&gt; has e.g. a keypair quota of &lt;code&gt;5&lt;/code&gt;, while
user &lt;code&gt;bar&lt;/code&gt; has a keypair quota of &lt;code&gt;6&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;quota-classes&#34;&gt;Quota classes&lt;/h2&gt;
&lt;p&gt;Finally, nova and cinder also have the concept of quota classes. The idea
behind these was to allow for a two-level hierarchy of quotas, where you could
define different &amp;ldquo;classes&amp;rdquo; of default quota and specific what class a project
would get. For example, you could have three quota classes - gold, silver and
bronze - and a project would get assigned to one of these quota classes
depending on how much the customer was paying. This was seen as an easier
alternative to setting defaults for each project individually. However,
actually using quota classes required a separate out-of-tree service that would
set a &lt;code&gt;quota_class&lt;/code&gt; attribute in the request context. Rackspace apparently had
one such service called &lt;em&gt;Turnstile&lt;/em&gt; that did this but no one else appears to
have implemented something like this and efforts to remove the need for an
external service &lt;a href=&#34;https://bugs.launchpad.net/nova/+bug/969537&#34;&gt;never went
anywhere&lt;/a&gt;. In effect, this feature
was never fully implemented and both nova and cinder only support the &lt;code&gt;default&lt;/code&gt;
quota class while neutron never even tried implementing this. It&amp;rsquo;s an
irrelevance nowadays.&lt;/p&gt;

&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;For more information on the background of &lt;em&gt;quota classes&lt;/em&gt;, the following
mailing lists threads are helpful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.mail-archive.com/openstack@lists.launchpad.net/msg09237.html&#34;&gt;Quota classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.mail-archive.com/openstack-dev@lists.openstack.org/msg124494.html&#34;&gt;Does ANYONE at all use the quota class functionality in Nova?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;quota-usage&#34;&gt;Quota usage&lt;/h2&gt;
&lt;p&gt;Obviously applying quotas to projects and users is one thing, but we actually
need to track that usage somewhere. This also happens at the project level.
The nova, cinder and neutron projects all track both a &lt;code&gt;reserved&lt;/code&gt; value and an
&lt;code&gt;in_use&lt;/code&gt; value. In nova&amp;rsquo;s case, the &lt;code&gt;reserved&lt;/code&gt; value was previously used to
reserve a resource at the API layer (&lt;code&gt;nova-api&lt;/code&gt;) before committing them at the
compute layer (&lt;code&gt;nova-compute&lt;/code&gt;). Nova moved away from this model in the 16.0.0
(Pike) release as part of the work to introduce cells v2, and the &lt;code&gt;reserved&lt;/code&gt;
value will now always be &lt;code&gt;0&lt;/code&gt;. This effort was tracked in the &lt;a href=&#34;https://specs.openstack.org/openstack/nova-specs/specs/pike/implemented/cells-count-resources-to-check-quota-in-api.html&#34;&gt;&lt;em&gt;Count resources
to check quota in API for cells&lt;/em&gt; spec&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the case of nova, much of this information is gleaned either from the
database or the placement service, depending on configuration. I suspect most
services use a similar model. This usage information is available to the user
via various APIs or &lt;em&gt;openstackclient&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;quota-drivers&#34;&gt;Quota drivers&lt;/h2&gt;
&lt;p&gt;The nova, cinder and neutron projects all have the concept of quota drivers. By
default, all projects use a DB-based quota driver but both nova and neutron
offer alternative drivers. I haven&amp;rsquo;t gone into detail on these here since it&amp;rsquo;s
not entirely relevant to this discussion. You should refer to the
&lt;a href=&#34;https://docs.openstack.org/nova/latest/configuration/config.html#quota.driver&#34;&gt;nova&lt;/a&gt; and &lt;a href=&#34;https://docs.openstack.org/neutron/latest/configuration/neutron.html#quotas.quota_driver&#34;&gt;neutron&lt;/a&gt; configuration
documentation for more information if interested though.&lt;/p&gt;
&lt;h2 id=&#34;how-do-i-use-the-damn-thing&#34;&gt;How do I use the damn thing?&lt;/h2&gt;
&lt;p&gt;The best way to interact with quotas is using a tool that tries to abstract all
of the above craziness from you. To this end, I&amp;rsquo;d recommend &lt;em&gt;openstackclient&lt;/em&gt;.
While OSC has supported quotas for years, recent versions of this (v6.1.0 or
later) have improved the UX further and contain some important feature
additions, like the ability to view quota usage for the cinder service.&lt;/p&gt;
&lt;p&gt;Firstly, let&amp;rsquo;s create a project-specific quota for, say, the number of
instances.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack quota set --instances &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; $OS_PROJECT_NAME
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can inspect those quotas:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack quota show $OS_PROJECT_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Resource              | Limit |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| cores                 |    &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| instances             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| ram                   | &lt;span style=&#34;color:#ae81ff&#34;&gt;51200&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| volumes               |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snapshots             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;43&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| gigabytes             |  &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| backups               |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| volumes_lvmdriver-1   |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| gigabytes_lvmdriver-1 |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snapshots_lvmdriver-1 |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| volumes___DEFAULT__   |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| gigabytes___DEFAULT__ |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snapshots___DEFAULT__ |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| groups                |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| networks              |   &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| ports                 |   &lt;span style=&#34;color:#ae81ff&#34;&gt;500&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| rbac_policies         |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| routers               |    &lt;span style=&#34;color:#ae81ff&#34;&gt;21&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| subnets               |   &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| subnet_pools          |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| fixed-ips             |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| injected-file-size    | &lt;span style=&#34;color:#ae81ff&#34;&gt;10240&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| injected-path-size    |   &lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| injected-files        |     &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| key-pairs             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;33&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| properties            |   &lt;span style=&#34;color:#ae81ff&#34;&gt;128&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| server-groups         |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| server-group-members  |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| floating-ips          |    &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| secgroup-rules        |   &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| secgroups             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| backup-gigabytes      |  &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| per-volume-gigabytes  |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also include usage information if you want:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack quota show --usage $OS_PROJECT_NAME
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------------+-------+--------+----------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Resource              | Limit | In Use | Reserved |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------------+-------+--------+----------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| cores                 |    &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| instances             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| ram                   | &lt;span style=&#34;color:#ae81ff&#34;&gt;51200&lt;/span&gt; |   &lt;span style=&#34;color:#ae81ff&#34;&gt;4096&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| volumes               |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snapshots             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;43&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| gigabytes             |  &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| backups               |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| volumes_lvmdriver-1   |    -1 |      &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| gigabytes_lvmdriver-1 |    -1 |      &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snapshots_lvmdriver-1 |    -1 |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| volumes___DEFAULT__   |    -1 |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| gigabytes___DEFAULT__ |    -1 |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snapshots___DEFAULT__ |    -1 |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| groups                |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| networks              |   &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| ports                 |   &lt;span style=&#34;color:#ae81ff&#34;&gt;500&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| rbac_policies         |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| routers               |    &lt;span style=&#34;color:#ae81ff&#34;&gt;21&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| subnets               |   &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| subnet_pools          |    -1 |      &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| fixed-ips             |    -1 |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| injected-file-size    | &lt;span style=&#34;color:#ae81ff&#34;&gt;10240&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| injected-path-size    |   &lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| injected-files        |     &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| key-pairs             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;33&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| properties            |   &lt;span style=&#34;color:#ae81ff&#34;&gt;128&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| server-groups         |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| server-group-members  |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| floating-ips          |    &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| secgroup-rules        |   &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| secgroups             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| backup-gigabytes      |  &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| per-volume-gigabytes  |    -1 |      &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------------+-------+--------+----------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Default quotas are applied to each project unless there are project-specific
quotas to override them. These can be inspected also:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack quota show --default
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Resource              | Limit |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| cores                 |    &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| instances             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| ram                   | &lt;span style=&#34;color:#ae81ff&#34;&gt;51200&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| volumes               |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snapshots             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;43&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| gigabytes             |  &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| backups               |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| volumes_lvmdriver-1   |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| gigabytes_lvmdriver-1 |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snapshots_lvmdriver-1 |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| volumes___DEFAULT__   |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| gigabytes___DEFAULT__ |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| snapshots___DEFAULT__ |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| groups                |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| networks              |   &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| ports                 |   &lt;span style=&#34;color:#ae81ff&#34;&gt;500&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| rbac_policies         |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| routers               |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| subnets               |   &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| subnet_pools          |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| fixed-ips             |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| injected-file-size    | &lt;span style=&#34;color:#ae81ff&#34;&gt;10240&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| injected-path-size    |   &lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| injected-files        |     &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| key-pairs             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;33&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| properties            |   &lt;span style=&#34;color:#ae81ff&#34;&gt;128&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| server-groups         |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| server-group-members  |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| floating-ips          |    &lt;span style=&#34;color:#ae81ff&#34;&gt;50&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| secgroup-rules        |   &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| secgroups             |    &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| backup-gigabytes      |  &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| per-volume-gigabytes  |    -1 |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-----------------------+-------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can list quotas for multiple projects. Note that when doing this, only the
projects with project-specific quotas are shown to avoid dumping potentially
thousands of lines of duplicate, useless info to the terminal.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack quota list --compute
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------------------------+-------+-----------+----------------+-----------------------------+--------------------------+-----------+-----------+----------------+-------+---------------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Project ID                       | Cores | Fixed IPs | Injected Files | Injected File Content Bytes | Injected File Path Bytes | Instances | Key Pairs | Metadata Items |   Ram | Server Groups | Server Group Members |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------------------------+-------+-----------+----------------+-----------------------------+--------------------------+-----------+-----------+----------------+-------+---------------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 700a8fa37f154153809be9d1814d8625 |    &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt; |        -1 |              &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; |                       &lt;span style=&#34;color:#ae81ff&#34;&gt;10240&lt;/span&gt; |                      &lt;span style=&#34;color:#ae81ff&#34;&gt;255&lt;/span&gt; |         &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt; |        &lt;span style=&#34;color:#ae81ff&#34;&gt;33&lt;/span&gt; |            &lt;span style=&#34;color:#ae81ff&#34;&gt;128&lt;/span&gt; | &lt;span style=&#34;color:#ae81ff&#34;&gt;51200&lt;/span&gt; |            &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |                   &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------------------------+-------+-----------+----------------+-----------------------------+--------------------------+-----------+-----------+----------------+-------+---------------+----------------------+
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, you can unset configured quotas:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;❯ openstack quota delete $OS_PROJECT_NAME
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;Many of these commands have additional flags that might be relevant. Pass the
&lt;code&gt;--help&lt;/code&gt; option to view the help text for the option.&lt;/div&gt;
&lt;/aside&gt;

&lt;h2 id=&#34;addendum-unified-limits&#34;&gt;Addendum! Unified limits&lt;/h2&gt;
&lt;p&gt;If you consider the above rather confusing, you should try to using it in
real-world environments! The deficiencies with the existing quota model were
brought up in multiple discussions with operators and end users over the years,
and the end result of these discussions was a concept known as &lt;em&gt;unified
limits&lt;/em&gt;. As the name would suggest, the aim of this feature is to provide a
unified model for maintaining resource limits across multiple services. Good
&lt;a href=&#34;https://docs.openstack.org/project-team-guide/technical-guides/unified-limits.html&#34;&gt;documentation&lt;/a&gt; exists to describe the benefits of this approach
over the status quo. Adding support for unified limits requires significant
effort on the end of the individual projects, and so far only the compute
project appears to have started down this path, introducing preliminary support
for unified limits during the &lt;a href=&#34;https://specs.openstack.org/openstack/nova-specs/specs/yoga/implemented/unified-limits-nova.html&#34;&gt;yoga release&lt;/a&gt;. This remains in
development and I&amp;rsquo;m not sure when we plan to finish it. The above guide will
probably be useful for some time to come 😅&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SQLAlchemy Relationships Without Foreign Keys (And How to Replace &#39;backref&#39; With &#39;back_populates&#39;)</title>
      <link>https://that.guru/blog/sqlalchemy-relationships-without-foreign-keys/</link>
      <pubDate>Fri, 07 Oct 2022 00:00:00 +0000</pubDate>
      
      <guid>https://that.guru/blog/sqlalchemy-relationships-without-foreign-keys/</guid>
      <description>&lt;p&gt;When implementing relationships in SQLAlchemy models, you will generally need
to cross-reference one model from another, ideally in a bidirectional manner.
Historically, the mechanism to do this has been the &lt;code&gt;relationship.backref&lt;/code&gt;
keyword. However, as noted in the &lt;a href=&#34;https://docs.sqlalchemy.org/en/14/orm/backref.html&#34;&gt;SQLAlchemy
documentation&lt;/a&gt;, this keyword should now be considered legacy
and the &lt;code&gt;relationship.back_populates&lt;/code&gt; migration preferred instead. There are a
couple of reasons for this. Quoting from the docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;a href=&#34;https://docs.sqlalchemy.org/en/14/orm/relationship_api.html#sqlalchemy.orm.relationship.params.backref&#34;&gt;&lt;code&gt;relationship.backref&lt;/code&gt;&lt;/a&gt; keyword should be considered legacy,
and use of &lt;a href=&#34;https://docs.sqlalchemy.org/en/14/orm/relationship_api.html#sqlalchemy.orm.relationship.params.back_populates&#34;&gt;&lt;code&gt;relationship.back_populates&lt;/code&gt;&lt;/a&gt; with explicit
&lt;a href=&#34;https://docs.sqlalchemy.org/en/14/orm/relationship_api.html#sqlalchemy.orm.relationship&#34;&gt;&lt;code&gt;relationship()&lt;/code&gt;&lt;/a&gt; constructs should be preferred. Using
individual &lt;code&gt;relationship()&lt;/code&gt; constructs provides advantages including that
both ORM mapped classes will include their attributes up front as the class
is constructed, rather than as a deferred step, and configuration is more
straightforward as all arguments are explicit. New &lt;a href=&#34;https://peps.python.org/pep-0484/&#34;&gt;&lt;strong&gt;PEP 484&lt;/strong&gt;&lt;/a&gt;
features in SQLAlchemy 2.0 also take advantage of attributes being explicitly
present in source code rather than using dynamic attribute generation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The SQLAlchemy docs &lt;a href=&#34;https://docs.sqlalchemy.org/en/14/orm/backref.html&#34;&gt;include a guide on migrating tables&lt;/a&gt;,
(as well as a great overview on &lt;a href=&#34;https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html&#34;&gt;relationships in
general&lt;/a&gt;), however, this guide assumes you are using
foreign key constraints in your models and using the auto-incrementing primary
key field as your reference. To be honest, you probably should be using foreign
keys since without these &lt;a href=&#34;https://stackoverflow.com/questions/3433975/why-use-foreign-key-constraints-in-mysql&#34;&gt;you have no way to enforce referential
integrity&lt;/a&gt; but say you can&amp;rsquo;t use them or don&amp;rsquo;t want to, how does
one migrate? The below examples should hopefully guide you in migrating these.&lt;/p&gt;
&lt;h2 id=&#34;one-to-many&#34;&gt;One-to-Many&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;one-to-many&lt;/em&gt; pattern is the simpler one to migrate. Consider the following
example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; uuid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; orm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BASE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;declarative_base()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;User&lt;/span&gt;(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    uuid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Address&lt;/span&gt;(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;addresses&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tableargs__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Index(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;addresses_user_uuid_idx&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_uuid&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_uuid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;User&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        backref&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;addresses&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        primaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Address.user_uuid == User.uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        foreign_keys&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;user_uuid,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    engine &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_engine(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sqlite://&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sessionmaker(bind&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;engine)()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BASE&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;metadata&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_all(engine)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; User(name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;John Doe&amp;#39;&lt;/span&gt;, uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;str(uuid&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;uuid4()))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(user)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Address(user_uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;user&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;uuid)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(address)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;# Users&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; user &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query(User)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;User: name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;user&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;# Addresses&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; address &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query(Address)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Address: user=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;address&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;user_uuid&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before looking at migrating from &lt;code&gt;backref&lt;/code&gt; to &lt;code&gt;back_populates&lt;/code&gt;, it&amp;rsquo;s worth
understanding what&amp;rsquo;s going on here. You&amp;rsquo;ll note that we have a relationship
from the &lt;code&gt;Address&lt;/code&gt; model back to the &lt;code&gt;User&lt;/code&gt; model. This has the &lt;code&gt;backref&lt;/code&gt;
keyword set on it, allowing you to reference related addresses from instances
of &lt;code&gt;User&lt;/code&gt;, however it also has &lt;code&gt;primaryjoin&lt;/code&gt; and &lt;code&gt;foreign_keys&lt;/code&gt; keywords set.
These are necessary because the &lt;code&gt;user_uuid&lt;/code&gt; column does not have a foreign key
constraint present. You can confirm this by inspecting the schemas generated by
SQLAlchemy.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❯ python
&amp;gt;&amp;gt;&amp;gt; import test_o2m  # name of module saved locally
&amp;gt;&amp;gt;&amp;gt; from sqlalchemy import schema
&amp;gt;&amp;gt;&amp;gt; print(schema.CreateTable(test_o2m.User.__table__))

CREATE TABLE &amp;#34;user&amp;#34; (
        id INTEGER NOT NULL,
        uuid VARCHAR(36) NOT NULL,
        name VARCHAR,
        PRIMARY KEY (id)
)


&amp;gt;&amp;gt;&amp;gt; print(schema.CreateTable(test_o2m.Address.__table__))

CREATE TABLE addresses (
        id INTEGER NOT NULL,
        user_uuid VARCHAR(36) NOT NULL,
        PRIMARY KEY (id)
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Because we don&amp;rsquo;t have a foreign key present, SQLAlchemy isn&amp;rsquo;t able to determine
the join condition. This necessitates the addition of the &lt;code&gt;primaryjoin&lt;/code&gt; and
&lt;code&gt;foreign_keys&lt;/code&gt; keywords to guide SQLAlchemy on resolving these relations.
Removing these keywords will yield an error message.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sqlalchemy.exc.NoForeignKeysError: Can&amp;#39;t find any foreign key relationships between &amp;#39;addresses&amp;#39; and &amp;#39;user&amp;#39;.
&lt;/code&gt;&lt;/pre&gt;
&lt;aside class=&#34;admonition note&#34;&gt;
  &lt;div class=&#34;admonition-content&#34;&gt;&lt;p&gt;An alternative to the &lt;code&gt;foreign_keys&lt;/code&gt; argument is to annotate the join condition
with the &lt;code&gt;foreign()&lt;/code&gt; annotation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;user &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;User&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    backref&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;addresses&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    primaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Address.user_uuid == foreign(User.uuid)&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/aside&gt;

&lt;p&gt;The only other unusual thing here is that we&amp;rsquo;re using a field other than &lt;code&gt;id&lt;/code&gt;
to reference things, namely a &lt;code&gt;uuid&lt;/code&gt; field on both models. There&amp;rsquo;s no real
reason to do this here but this was the case in the models that I had to
migrate and it makes things a little more complicated and &amp;ldquo;real-world&amp;rsquo;y&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Now that we understand what&amp;rsquo;s going, it&amp;rsquo;s time to look at switching from
&lt;code&gt;backref&lt;/code&gt; to &lt;code&gt;back_populates&lt;/code&gt;. It turns out to be pretty simple for one-to-many
models like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; uuid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; orm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BASE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;declarative_base()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;User&lt;/span&gt;(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    uuid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addresses &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Address&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        primaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;User.uuid == Address.user_uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        back_populates&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        foreign_keys&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Address.user_uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Address&lt;/span&gt;(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;addresses&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tableargs__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Index(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;addresses_user_uuid_idx&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;user_uuid&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user_uuid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;User&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        primaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Address.user_uuid == User.uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        back_populates&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;addresses&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        foreign_keys&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;user_uuid,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    engine &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_engine(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sqlite://&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sessionmaker(bind&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;engine)()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BASE&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;metadata&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_all(engine)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    user &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; User(name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;John Doe&amp;#39;&lt;/span&gt;, uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;str(uuid&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;uuid4()))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(user)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    address &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Address(user_uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;user&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;uuid)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(address)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;# Users&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; user &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query(User)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;User: name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;user&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;# Addresses&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; address &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query(Address)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Address: user=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;address&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;user_uuid&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Looking at the diff for these two files, we see the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;diff --git test_o2m.py test_o2m.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index 4f23221..f865045 100644
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--- test_o2m.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+++ test_o2m.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -14,6 +14,13 @@ class User(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;     uuid = sa.Column(sa.String(36), nullable=False)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     name = sa.Column(sa.String)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+    addresses = orm.relationship(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        &amp;#39;Address&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        primaryjoin=&amp;#39;User.uuid == Address.user_uuid&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        back_populates=&amp;#39;user&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        foreign_keys=&amp;#39;Address.user_uuid&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+    )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; class Address(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     __tablename__ = &amp;#39;addresses&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -26,8 +33,8 @@ class Address(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     user = orm.relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         &amp;#39;User&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-        backref=&amp;#39;addresses&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;         primaryjoin=&amp;#39;Address.user_uuid == User.uuid&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        back_populates=&amp;#39;addresses&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;         foreign_keys=user_uuid,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So for the &lt;code&gt;Address&lt;/code&gt; model, we have simply replaced the &lt;code&gt;backref&lt;/code&gt; keyword with
&lt;code&gt;back_populates&lt;/code&gt;. For the &lt;code&gt;User&lt;/code&gt; model, we have added a new relationship. This
is mostly the inverse of the relationship on the &lt;code&gt;Address&lt;/code&gt; model and only the
&lt;code&gt;foreign_key&lt;/code&gt; argument points to the same field (a foreign field in the
&lt;code&gt;Address&lt;/code&gt; model case).&lt;/p&gt;
&lt;p&gt;Overall, this isn&amp;rsquo;t a difficult migration. Migrating many-to-many models could
be a little trickier though. Let&amp;rsquo;s see.&lt;/p&gt;
&lt;h2 id=&#34;many-to-many&#34;&gt;Many-to-Many&lt;/h2&gt;
&lt;p&gt;Now consider the following example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; uuid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; orm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BASE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;declarative_base()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;association_table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Table(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;association&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BASE&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;metadata,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;parent_uuid&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;child_uuid&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Parent&lt;/span&gt;(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;parents&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __table_args__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Index(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;parents_uuid_idx&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;uuid&amp;#39;&lt;/span&gt;, unique&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    uuid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Child&lt;/span&gt;(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;children&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __table_args__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Index(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;children_uuid_idx&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;uuid&amp;#39;&lt;/span&gt;, unique&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    uuid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parents &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Parent&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        secondary&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;association&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        primaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Child.uuid == association.c.child_uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        secondaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;association.c.parent_uuid == Parent.uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        backref&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;children&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    engine &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_engine(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sqlite://&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sessionmaker(bind&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;engine)()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BASE&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;metadata&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_all(engine)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Parent(name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;John Doe&amp;#39;&lt;/span&gt;, uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;str(uuid&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;uuid4()))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(parent)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    child &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Child(name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Jimmy Doe&amp;#39;&lt;/span&gt;, uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;str(uuid&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;uuid4()))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(child)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parent&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;children&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(child)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;# Parents&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; parent &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query(Parent)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        children &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [x&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; x &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; parent&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;children]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Parent: name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;parent&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, children=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;children&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;# Children&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; child &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query(Child)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parents &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [x&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; x &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; child&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parents]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Child: name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;child&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, parents=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;parents&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, there are a couple of things to consider here. Firstly, since this is a
many-to-many relationship, we need a so-called &lt;em&gt;join table&lt;/em&gt; (also known as a
&lt;em&gt;association table&lt;/em&gt; in SQLAlchemy or a &lt;em&gt;through table&lt;/em&gt; in Django). The
necessitates the use of the &lt;code&gt;secondary&lt;/code&gt; keyword to indicate the association
table. Next up, once again we do not have a foreign key constraint present
meaning we need to inform SQLAlchemy how the join should be managed. The use of
the association table means we must specify not only &lt;code&gt;primaryjoin&lt;/code&gt; but also
&lt;code&gt;secondaryjoin&lt;/code&gt;: the former describes the relationship from &lt;code&gt;Child&lt;/code&gt; to the
association table, while the latter describes the relationship from the
association table to &lt;code&gt;Parent&lt;/code&gt;. Finally, once again we&amp;rsquo;re using a &lt;code&gt;uuid&lt;/code&gt; field
for referencing to makes things a little more like the real world.&lt;/p&gt;
&lt;p&gt;Now to switch from &lt;code&gt;backref&lt;/code&gt; to &lt;code&gt;back_populates&lt;/code&gt; once again.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; uuid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; sa
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; sqlalchemy &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; orm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BASE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;declarative_base()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;association_table &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Table(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;association&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BASE&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;metadata,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;parent_uuid&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;child_uuid&amp;#39;&lt;/span&gt;, sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Parent&lt;/span&gt;(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;parents&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __table_args__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Index(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;parents_uuid_idx&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;uuid&amp;#39;&lt;/span&gt;, unique&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    uuid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    children &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Child&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        secondary&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;association&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        primaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Parent.uuid == association.c.parent_uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        secondaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;association.c.child_uuid == Child.uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        back_populates&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;parents&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Child&lt;/span&gt;(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __tablename__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;children&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    __table_args__ &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Index(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;children_uuid_idx&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;uuid&amp;#39;&lt;/span&gt;, unique&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Integer, primary_key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    uuid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String(&lt;span style=&#34;color:#ae81ff&#34;&gt;36&lt;/span&gt;), nullable&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Column(sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;String)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parents &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;relationship(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Parent&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        secondary&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;association&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        primaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Child.uuid == association.c.child_uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        secondaryjoin&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;association.c.parent_uuid == Parent.uuid&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        back_populates&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;children&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    engine &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sa&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_engine(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;sqlite://&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; orm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sessionmaker(bind&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;engine)()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    BASE&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;metadata&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;create_all(engine)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Parent(name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;John Doe&amp;#39;&lt;/span&gt;, uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;str(uuid&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;uuid4()))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(parent)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    child &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Child(name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Jimmy Doe&amp;#39;&lt;/span&gt;, uuid&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;str(uuid&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;uuid4()))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add(child)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;commit()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    parent&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;children&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(child)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;# Parents&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; parent &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query(Parent)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        children &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [x&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; x &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; parent&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;children]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Parent: name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;parent&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, children=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;children&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;# Children&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; child &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; session&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;query(Child)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        parents &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [x&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; x &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; child&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parents]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        print(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Child: name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;child&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;, parents=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;parents&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    print()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; __name__ &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Looking at the diff for these two files, we see the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;diff --git test_m2m.py test_m2m.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;index 948942f..d1dc0d4 100644
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--- test_m2m.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+++ test_m2m.py
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -25,6 +25,14 @@ class Parent(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;     uuid = sa.Column(sa.String(36), nullable=False)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     name = sa.Column(sa.String)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+    children = orm.relationship(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        &amp;#39;Child&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        secondary=&amp;#39;association&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        primaryjoin=&amp;#39;Parent.uuid == association.c.parent_uuid&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        secondaryjoin=&amp;#39;association.c.child_uuid == Child.uuid&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        back_populates=&amp;#39;parents&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+    )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; class Child(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     __tablename__ = &amp;#39;children&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;@@ -41,7 +49,7 @@ class Child(BASE):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;         secondary=&amp;#39;association&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         primaryjoin=&amp;#39;Child.uuid == association.c.child_uuid&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         secondaryjoin=&amp;#39;association.c.parent_uuid == Parent.uuid&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-        backref=&amp;#39;children&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;+        back_populates=&amp;#39;children&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;&lt;/span&gt;     )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is nearly identical to the changes required for migrating the one-to-many
models. Once again, we simply replace &lt;code&gt;backref&lt;/code&gt; with &lt;code&gt;back_populates&lt;/code&gt; on the
relationship that is defined. We then define a new &lt;code&gt;relationship&lt;/code&gt; on the
&lt;code&gt;Parent&lt;/code&gt; model which has an inverse of the primary and secondary joins given
for the &lt;code&gt;Child&lt;/code&gt; model relationship.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;So that&amp;rsquo;s how you migrate from &lt;code&gt;backref&lt;/code&gt; to &lt;code&gt;back_populates&lt;/code&gt; on models without
foreign key constraints present. This likely isn&amp;rsquo;t broadly useful but hopefully
it will help someone that encounters this.&lt;/p&gt;
&lt;p&gt;The code for this blog post can be found on &lt;a href=&#34;https://github.com/stephenfin/sqlalchemy-relationships-demo&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
