<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.6.3">Jekyll</generator><link href="https://nate.coraor.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://nate.coraor.org/" rel="alternate" type="text/html" /><updated>2025-10-11T21:03:38+00:00</updated><id>https://nate.coraor.org/feed.xml</id><title type="html">lp0 on fire</title><subtitle>Modern system administration practices, curmudgeonly UNIX sympathies. No longer strictly POSIX-compliant.</subtitle><author><name>Nate Coraor</name></author><entry><title type="html">Leafing through CVMFS Catalogs</title><link href="https://nate.coraor.org/2025/04/08/leafing-through-cvmfs-catalogs.html" rel="alternate" type="text/html" title="Leafing through CVMFS Catalogs" /><published>2025-04-08T21:31:16+00:00</published><updated>2025-04-08T21:31:16+00:00</updated><id>https://nate.coraor.org/2025/04/08/leafing-through-cvmfs-catalogs</id><content type="html" xml:base="https://nate.coraor.org/2025/04/08/leafing-through-cvmfs-catalogs.html">&lt;p&gt;I’m mainly writing this down so I remember how to dig into these bits of CVMFS internals next time&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;As has happened a couple of times, I discovered today that the campus firewall was blocking one specific file on the
&lt;a href=&quot;https://cvmfs.readthedocs.io/en/stable/&quot;&gt;CVMFS&lt;/a&gt; Stratum 0 &lt;em&gt;and&lt;/em&gt; Stratum 1 servers hosted inside the campus network. Fetching the file from inside the
network works as expected, but from outside it fails:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[user@stratum1 ~]$&lt;/span&gt; cvmfs_server snapshot repo.example.org
&lt;span class=&quot;go&quot;&gt;Replicating from catalog at /gtdb
  Processing chunks [8056 registered chunks]: ........
failed to download http://stratum0.example.org/cvmfs/repo.example.org/data/73/2104e02468491fa4e61599c176b16c98a052d4P (15 - host serving data too slowly)
couldn't reach Stratum 0 - please check the network connection
terminate called after throwing an instance of 'ECvmfsException'
  what():  PANIC: /home/sftnight/jenkins/workspace/CvmfsFullBuildDocker/CVMFS_BUILD_ARCH/docker-x86_64/CVMFS_BUILD_PLATFORM/cc9/build/BUILD/cvmfs-2.11.5/cvmfs/swissknife_pull.cc : 286
Download error
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Additional testing with curl shows that no bytes are returned for the request and eventually the connection times out:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[user@stratum1 ~]$&lt;/span&gt; curl &lt;span class=&quot;nt&quot;&gt;-sv&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    http://stratum0.example.org/cvmfs/repo.example.org/data/73/2104e02468491fa4e61599c176b16c98a052d4P &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;span class=&quot;go&quot;&gt;*   Trying 192.0.2.42...
* TCP_NODELAY set
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;* Connected to stratum0.example.org (192.0.2.42) port 80 (#&lt;/span&gt;0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt; GET /cvmfs/repo.example.org/data/73/2104e02468491fa4e61599c176b16c98a052d4P HTTP/1.1
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt; Host: stratum0.example.org
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt; User-Agent: curl/7.61.1
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt; Accept: &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  0     0    0     0    0     0      0      0 --:--:--  0:10:13 --:--:--     0* Recv failure: Connection timed out
  0     0    0     0    0     0      0      0 --:--:--  0:10:14 --:--:--     0
* Closing connection 0
curl: (56) Recv failure: Connection timed out
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Upon describing the problem for the firewall admins, they sent back a few lines from the log showing the denial: the
file type (or “application” in Palo Altese) was detected as &lt;code class=&quot;highlighter-rouge&quot;&gt;flash&lt;/code&gt; (as in, Adobe/Shockwave Flash). Why is my CVMFS repo
full of public genomic data serving up Adobe Flash?&lt;/p&gt;

&lt;p&gt;Well, it’s not. CVMFS is a chunked, content-hash-addressed filesystem. I had recently published the &lt;a href=&quot;https://gtdb.ecogenomic.org/&quot;&gt;Genome Taxonomy
Database&lt;/a&gt; and suspected that the file in question (&lt;code class=&quot;highlighter-rouge&quot;&gt;2104e02468491fa4e61599c176b16c98a052d4P&lt;/code&gt;) to be a chunk from
that rather large database. But it’s best to confirm such suspicions.&lt;/p&gt;

&lt;p&gt;To do that, I need to know what file the chunk belongs to.&lt;/p&gt;

&lt;h1 id=&quot;locate-the-revision-that-added-the-chunk&quot;&gt;Locate the revision that added the chunk&lt;/h1&gt;

&lt;p&gt;There’s probably a more clever way to do this but I started with the mod date on the hash file:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[user@stratum0 ~]$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lh&lt;/span&gt; /srv/cvmfs/repo.example.org/data/73/2104e02468491fa4e61599c176b16c98a052d4P
&lt;span class=&quot;go&quot;&gt;-rw-r--r-- 1 user user 2.4M Apr  4 14:04 /srv/cvmfs/repo.example.org/data/73/2104e02468491fa4e61599c176b16c98a052d4P
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This should point to the revision/tag (unless you have a lot of them around that time):&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[user@stratum0 ~]$&lt;/span&gt; cvmfs_server tag repo.example.org | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-B1&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'4 Apr 2025'&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;stuff | 41 | 1 Apr 2025 12:34:05  |        | Stuff
gtdb  │ 42 │ 4 Apr 2025 14:09:38  │        │ GTDB database

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;identify-files-changed-in-that-revision&quot;&gt;Identify files changed in that revision&lt;/h1&gt;

&lt;p&gt;The diff subcommand shows me what files were added, removed, or changed:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[user@stratum0 ~]$&lt;/span&gt; cvmfs_server diff &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; stuff &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; gtdb repo.example.org
&lt;span class=&quot;gp&quot;&gt;d(#&lt;/span&gt; regular files&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 10
&lt;span class=&quot;gp&quot;&gt;d(#&lt;/span&gt; symlinks&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 0
&lt;span class=&quot;gp&quot;&gt;d(#&lt;/span&gt; directories&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 1
&lt;span class=&quot;gp&quot;&gt;d(#&lt;/span&gt; catalogs&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 1
&lt;span class=&quot;go&quot;&gt;/ modify directory [link-count, timestamp]
/gtdb add directory +4096 bytes
/gtdb/GTDB add file +33731108154 bytes
/gtdb/GTDB_h add file +13565000049 bytes
/gtdb/GTDB.index add file +2601811970 bytes
/gtdb/GTDB.dbtype add file +4 bytes
/gtdb/GTDB.lookup add file +3621260940 bytes
/gtdb/GTDB.source add file +786640 bytes
/gtdb/GTDB.version add file +28 bytes
/gtdb/GTDB_h.index add file +2559366433 bytes
/gtdb/GTDB_mapping add file +1562822676 bytes
/gtdb/.cvmfscatalog add file +0 bytes
/gtdb/GTDB_h.dbtype add file +4 bytes
/gtdb/GTDB_taxonomy add file +9978672 bytes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;locate-the-catalog-for-the-offending-chunk&quot;&gt;Locate the catalog for the offending chunk&lt;/h1&gt;

&lt;p&gt;Knowing what’s changed, now I can locate the catalog corresponding to those changes:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[user@stratum0 ~]$&lt;/span&gt; cvmfs_server list-catalogs &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; repo.example.org
&lt;span class=&quot;go&quot;&gt;218f6ec14c5f8cf2028d9f419e57157f0dbe8d2d /
├─ ae762dea060a55e6d1d075168bbbf8cd440ca6b2 /gtdb
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;extract-the-catalog-and-verify-the-filename&quot;&gt;Extract the catalog and verify the filename&lt;/h1&gt;

&lt;p&gt;With the catalog identified, it needs to be extracted:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[user@stratum0 ~]$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /srv/cvmfs/repo.example.org/data/ae/762dea060a55e6d1d075168bbbf8cd440ca6b2C &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    | cvmfs_swissknife zpipe &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; /tmp/catalog.sqlite
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I can then verify that the chunk belongs to the &lt;code class=&quot;highlighter-rouge&quot;&gt;GTDB&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[user@stratum0 ~]$ sqlite3 /tmp/catalog.sqlite
SQLite version 3.34.1 2021-01-20 14:10:07
Enter &quot;.help&quot; for usage hints.
sqlite&amp;gt; SELECT c.name
FROM chunks ch
JOIN catalog c
  ON ch.md5path_1 = c.md5path_1 AND ch.md5path_2 = c.md5path_2
WHERE ch.hash = X'ae762dea060a55e6d1d075168bbbf8cd440ca6b2';
GTDB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;check-file-contents&quot;&gt;Check file contents&lt;/h1&gt;

&lt;p&gt;But why stop there? A little digging suggests that the file magic for a Flash file should be ASCII &lt;code class=&quot;highlighter-rouge&quot;&gt;CWS&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;FWS&lt;/code&gt;, or
&lt;code class=&quot;highlighter-rouge&quot;&gt;FLV&lt;/code&gt;, so let’s confirm:&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;[user@stratum0 ~]$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /srv/cvmfs/repo.example.org/data/73/2104e02468491fa4e61599c176b16c98a052d4P &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    | cvmfs_swissknife zpipe &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; | head &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 3 &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;CWS
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And there we are, a Flash application, according to Palo Alto. Viewing the rest of the contents of the chunk also
confirm that the data after &lt;code class=&quot;highlighter-rouge&quot;&gt;CWS&lt;/code&gt; do not resemble Adobe Flash.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;I do confess, I let ChatGPT have a crack at it and it gave me some comically wrong answers, including: 1. making up the command &lt;code class=&quot;highlighter-rouge&quot;&gt;cvmfs_find &amp;lt;repo&amp;gt; --catalogs&lt;/code&gt;, which it claims is a “helper tool to search metadata, including nested catalogs”, 2. making up the &lt;code class=&quot;highlighter-rouge&quot;&gt;cvmfs_server catalog-chroot&lt;/code&gt; subcommand “to mount the catalog structure”, and 3. making up the &lt;code class=&quot;highlighter-rouge&quot;&gt;cvmfs_swissknife cat&lt;/code&gt; subcommand to extract catalogs. Oh you wacky LLM and your confidently incorrect assertions. You’d make a great redditor. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>Nate Coraor</name></author><category term="cvmfs" /><category term="firewall" /><summary type="html">I’m mainly writing this down so I remember how to dig into these bits of CVMFS internals next time1. I do confess, I let ChatGPT have a crack at it and it gave me some comically wrong answers, including: 1. making up the command cvmfs_find &amp;lt;repo&amp;gt; --catalogs, which it claims is a “helper tool to search metadata, including nested catalogs”, 2. making up the cvmfs_server catalog-chroot subcommand “to mount the catalog structure”, and 3. making up the cvmfs_swissknife cat subcommand to extract catalogs. Oh you wacky LLM and your confidently incorrect assertions. You’d make a great redditor. &amp;#8617;</summary></entry><entry><title type="html">Simon Gladman</title><link href="https://nate.coraor.org/2022/11/29/simon-gladman.html" rel="alternate" type="text/html" title="Simon Gladman" /><published>2022-11-30T01:01:40+00:00</published><updated>2022-11-30T01:01:40+00:00</updated><id>https://nate.coraor.org/2022/11/29/simon-gladman</id><content type="html" xml:base="https://nate.coraor.org/2022/11/29/simon-gladman.html">&lt;p&gt;In the 24 or so hours that have passed since I learned of the passing of my friend Simon, a lot of grief, anger,
stories, and thoughts have bounced around my head. It’s become so awash that I decided I needed to write something down,
if for no other reason than to help process, and now it has become a sort of memorial for him. My version of one,
anyway, as this has become deeply personal and I have debated over whether I’d share it publicly. I have shared it with
some other folks close to Simon and it seems to have been helpful for them, so I’ve decided to post it. On top of that,
I want the world to know what an incredible guy Simon was.&lt;/p&gt;

&lt;p&gt;I thought writing this would be hard, and it is - the thoughts are there, in my head for hours as I’ve had a long drive
to make and thus too much time inside my own head, but I don’t know if I can vocalize them well. But in a way, it’s also
not hard, because there is just so much you can say about Simon. But I write and then I edit, and write more, edit more,
and I don’t know when it’s done. I don’t want it to be done.&lt;/p&gt;

&lt;p&gt;One of those thoughts I’d had was, how would I title a memorial to Simon? &lt;em&gt;“Have I told you about my watch?”&lt;/em&gt; was one of
the first things that came to mind. So it brought both a smile and a welling of tears upon opening &lt;a href=&quot;https://galaxyproject.org/news/2022-11-28-simon-gladman/&quot;&gt;the memorial that
Björn Grüning wrote for Galaxy&lt;/a&gt; only to discover that he had titled his the same. Sure, it’s fresh in
both our respective memories, but it’s also a perfect introduction of the person Simon was.&lt;/p&gt;

&lt;p&gt;Simon had a layover in Dubai on his way to the 2022 European Galaxy Days. This meant that he had the time to look at
some of the airport shops, and, unsurprisingly, a number of watches struck his eye. Simon loved watches, especially
fancy Seiko watches, and he came away with one of them. His delightful nerdish excitement to tell everyone and anyone
about his new watch became his running joke for the trip. “Have I told you about my watch?” he’d asked me (and many
others) with more than a little bit of amusement many times by the end of the week and a half we spent together.&lt;/p&gt;

&lt;p&gt;Simon was one of those once in a lifetime individuals who could (and did) befriend anyone. He had a disarming charm that
just flowed naturally, but he was genuinely interested in everyone and everything around him. He was the one who brought
everyone together, to collaborate, or to play games, or just to sit and talk and have a great time. That nerdish
excitement over his watch was how he was about everything, and it was infectious. I am not a watch person but I could
listen to Simon talk about his watch, or anything else, all day. His excitement over his favorite band, King Gizzard and
the Lizard Wizard (from his hometown of Melbourne, no less!) led me to check them out, and before long they were my
favorite band too. He told stories about his past adventures, both ours together and his with others, with the same
exuberance. He had a wonderful memory for such things, and I have the opposite, which meant that reliving those times
through his stories brought back many fantastic memories.&lt;/p&gt;

&lt;p&gt;The circumstances of my life have dwindled my friends list significantly over my middle aged years. It has become rare
for me to see my few remaining local friends. In some ways, a friendship with Simon was easier - there was no guilt over
the inability to see each other because we were on opposite sides of the world. So we had our friendship online, mainly
in a group chat with Helena, punctuated by the brief times once or so per year that we could be together in the real
world.&lt;/p&gt;

&lt;p&gt;Our chats were a great comfort to me. With Simon in Australia and Helena in Europe, we were almost evenly distributed
across time zones. I’d wake up to a log full of messages between the two of them to read, maybe catching Simon briefly
before he was off to bed. Helena and I would chat all day, and then toward the end of my day, Simon might reappear to
start his day and catch up on the chat that he had missed. It was asynchronous and wonky but it worked well enough for
the three of us, as we always had the anticipation of our next face-to-face meeting to keep us going. We had plans for a
week-long get-together in State College before the 2020 GCC in Toronto, only to have those plans crushed by COVID. So
too were the plans for hanging out in Belgium and the Netherlands after the 2021 GCC in Ghent (in Freiburg we had
discussed the possibility of a consolation prize: a Galaxy Admin Training in Ghent). When we finally did get to reunite
in Minneapolis and Freiburg in 2022, Simon’s hugs were long and crushing, and signified the truth of how difficult those
years apart had been, and how good it was to finally see eachother again.&lt;/p&gt;

&lt;p&gt;Simon and I were together on a fateful night in November 2016, in Salt Lake City, after teaching a day of the very first
Galaxy Admin Training. We went to a bar to watch the election results pour in, and stared with increasing dismay as what
seemed like a probably ok thing earlier in the day turned into a nightmare. I left early in a funk; the next morning I
met Simon at the tram stop and he cheered me up. He was always able to do that, he was a good listener and commiserate,
but he could steer things to a positive place in a respectful but effortless manner. In online chats, he was always
looking ahead, so excited for the next thing and the next time we’d be together.&lt;/p&gt;

&lt;p&gt;He cared deeply about his friends and would talk them up to anyone. I know more about his friend Maddi than I do about
some of my own friends, and I’ve never even met her. But Simon was so proud of her and her accomplishments that he
couldn’t help but talk about her the way a father talks excitedly about his child. He was unquestionably genuine in his
feelings.&lt;/p&gt;

&lt;p&gt;As we walked around Freiburg this past October, Simon told me how he’d love to spend a year working abroad as his kids
were reaching the age of independence and self-sufficiency. I think he was seriously considering it. He had gotten to a
relatively good place in life, in a happy relationship with a wonderful woman, two incredible kids that he loved dearly
and spoke about proudly and endlessly, and a job that he enjoyed and that afforded him opportunities to travel around
the world and make connections that truly seemed to mean everything to him.&lt;/p&gt;

&lt;p&gt;I used to imagine how my own life might go as I reached the same milestones. I imagined taking trips every year or so to
visit my two improbable friends, a Texan in Europe and an Australian. Simon was ten years older than me, Helena is ten
years younger, and yet somehow we all clicked, despite varying personal interests and lifestyles. I imagined a time 20
years from now, after Simon had retired and I no longer had the opportunity to meet up with him at conferences - we’d
see him at home in Australia, or plan trips together somewhere else.&lt;/p&gt;

&lt;p&gt;When I visited Melbourne in 2017 for the Galaxy Australasia Meeting, I had one extra day, and Simon drove us down the
breathtaking Great Ocean Road. I was reluctant to get in the water with Australia’s notoriously deadly ocean creatures,
but he reassured me it’d be fine, and I had a wonderful time. He was already planning us an extended trip down the coast
after the 2023 GCC, which in a great cruel irony, will finally come to Australia.&lt;/p&gt;

&lt;p&gt;The 2019 Galaxy Admin Training was held at Penn State in January/February. Simon and Helena came and experienced one of
the coldest weeks in recorded State College history, reaching temps of -20°F. I picked Simon up at the airport and - in
a story he loved to retell - he pulled a light puffer jacket out of a small stuff sack to show he’d prepared for this
winter trip. I just looked at him and shook my head. He and Helena both ended up borrowing my old coats for the duration
as neither of them had a need for such heavy coats at home. I drove my friends up to one of the scenic overlooks over
State College and we hurriedly took some photos before retreating back to the warmth of the car. Helena got “proper”
Tex-Mex for the first time since leaving the US. Both got to try the abomination known as a Pittsburgh-style sandwich.&lt;/p&gt;

&lt;p&gt;In addition to learning all of the unpleasantness that comes from being outdoors in horribly cold temperatures, Simon
also got to experience some of the fun. I took him up to a nearby state park, where some folks were clearing the snow
off a frozen pond to play hockey, and Simon had a blast sliding around on ice for the first time in his life.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet tw-align-center&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;⁦&lt;a href=&quot;https://twitter.com/SimonGladman1?ref_src=twsrc%5Etfw&quot;&gt;@SimonGladman1&lt;/a&gt;⁩ discovered sding on a frozen pond &lt;a href=&quot;https://t.co/604YD1vCTi&quot;&gt;pic.twitter.com/604YD1vCTi&lt;/a&gt;&lt;/p&gt;&amp;mdash; Nate Coraor (@natefoo@hachyderm.io) (@natefoo) &lt;a href=&quot;https://twitter.com/natefoo/status/1091820514400718849?ref_src=twsrc%5Etfw&quot;&gt;February 2, 2019&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;Later in the week we went ice skating (indoors thankfully!), and tried to go snow tubing but the temperature had swung
too warm, so we got coffee at Rothrock instead. Simon loved coffee more than anyone I know, and I could always count on
him to have already found a good coffee shop near every conference venue before I even arrived. More recently, we’d
gotten into the practice of trading beans every time we got together. I had worried about how I’d keep us all
entertained that week, but my worries ended up being silly in hindsight; Simon was happy just being together, no matter
what we did, and I realized that I was too.&lt;/p&gt;

&lt;p&gt;The circumstances of my life have meant that spending any significant extra time around conferences had been impossible,
until this October, when I was finally able to stay almost a week extra after the European Galaxy Days, and I will be
forever grateful that I did. On Sunday after the conference, Simon and I rode the train up to Basel. We had no plans and
no expectations due to Germany’s (and Switzerland’s) famed lack of Sunday openness, but I ended up having one of the
best days I can remember, just wandering the old city of Basel together. We walked around inside the historic Münster
and ultimately decided to take the climb up its 62.7m high &lt;em&gt;Martinsturm&lt;/em&gt; spire. Simon and I squeezed our portly frames
through the tiny spaces made for the tiny people of the Middle Ages; no small feat for either of us, but much trickier
for him at his towering height. We marveled at the sights over the city and he gave me his assessment of a fire he saw
off in the distance - Simon was a volunteer firefighter and loved to talk about that part of his life as much as I,
completely foreign to the subject, loved to hear about it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/simon-basel-munster.jpg&quot; alt=&quot;Simon and I on the Basler Münster&quot; /&gt;&lt;/p&gt;

&lt;p&gt;He was a bit appalled to learn that our vantage point was in fact only the first stop - around a narrow ledge with a low
wall, another door took us much further up the tower, to even more marvelous (if not a bit terrifying) views. After
retreating, with feet firmly back on cobblestone, we lucked into an idyllic lunch of Rösti with a beer outside on the
Münsterplatz at one of the only restaurants open on Sunday. I’ll remember that day for the rest of my life.&lt;/p&gt;

&lt;p&gt;Simon’s partner Tamara reached out to me secretly in advance of his 50th birthday, which was sadly occurring amidst
COVID isolation and lockdowns in Melbourne. She was compiling a video of all of his friends wishing him a happy
birthday, and asked if I could send the call out to all his friends in the Galaxy community. I racked my brain to think
who all those people might be and felt guilty over who I might be leaving off the list because I didn’t know if they
knew him that well, but it’s clear now I could have just sent it to everyone. As his good friend and colleague Andrew
Lonie &lt;a href=&quot;https://www.biocommons.org.au/news/simon-gladman&quot;&gt;so perfectly put it&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Simon was a people person first and foremost and made friends not just easily, but by default - if he met you, he
liked you and you liked him back - that’s just the way it was.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All the people whose lives Simon enriched are at a deep loss, and most of all his family, his children and partner who
he loved dearly. I miss you, Simon.&lt;/p&gt;</content><author><name>Nate Coraor</name></author><summary type="html">In the 24 or so hours that have passed since I learned of the passing of my friend Simon, a lot of grief, anger, stories, and thoughts have bounced around my head. It’s become so awash that I decided I needed to write something down, if for no other reason than to help process, and now it has become a sort of memorial for him. My version of one, anyway, as this has become deeply personal and I have debated over whether I’d share it publicly. I have shared it with some other folks close to Simon and it seems to have been helpful for them, so I’ve decided to post it. On top of that, I want the world to know what an incredible guy Simon was.</summary></entry><entry><title type="html">The Ansible Playbook you build isn’t the Ansible Playbook you run</title><link href="https://nate.coraor.org/2019/01/28/galaxy-admin-training-ansible.html" rel="alternate" type="text/html" title="The Ansible Playbook you build isn't the Ansible Playbook you run" /><published>2019-01-29T04:39:25+00:00</published><updated>2019-01-29T04:39:25+00:00</updated><id>https://nate.coraor.org/2019/01/28/galaxy-admin-training-ansible</id><content type="html" xml:base="https://nate.coraor.org/2019/01/28/galaxy-admin-training-ansible.html">&lt;h1 id=&quot;the-mistake&quot;&gt;The Mistake&lt;/h1&gt;

&lt;p&gt;A common practice of mine in developing &lt;a href=&quot;https://www.ansible.com/&quot;&gt;Ansible&lt;/a&gt; playbooks is to write a few pieces (add a role, write some
tasks, etc.), run the playbook, add some more, run again, and so on. At the end, I have a working playbook that I can
apply to other hosts - or so I think.&lt;/p&gt;

&lt;p&gt;The mistake is that running that playbook on another target runs the whole playbook at once, not incrementally as I did
when I was writing it. This means that unless I only ever add new tasks and roles after the ones I’ve already run, and I
never go back and make changes to earlier ones, &lt;em&gt;the order of tasks will be different&lt;/em&gt;, but that might not be obvious.
Here’s an example.&lt;/p&gt;

&lt;h1 id=&quot;example&quot;&gt;Example&lt;/h1&gt;

&lt;p&gt;This week some colleagues and I are teaching the &lt;a href=&quot;https://galaxyproject.org/events/2019-admin-training/&quot;&gt;2019 Galaxy Admin Training&lt;/a&gt; at Penn State. For this training, we
decided to do away with command line editing as much as possible (although a student may choose not to use Ansible when
they get home, the methods taught here apply to deployment and configuration management in general). This means we
(which is to say, mostly &lt;a href=&quot;https://github.com/erasche&quot;&gt;Helena&lt;/a&gt;) developed a &lt;a href=&quot;https://training.galaxyproject.org/topics/admin/tutorials/ansible-galaxy/tutorial.html&quot;&gt;training exercise&lt;/a&gt; for deploying &lt;a href=&quot;https://galaxyproject.org/&quot;&gt;Galaxy&lt;/a&gt; with
Ansible.&lt;/p&gt;

&lt;p&gt;As I do in development, we had the students build their playbooks step by step. At the end of the day, in preparation
for the next day, I ran the playbook on all the unused training cloud instances so that they’d be up to date, if anyone
needed to switch instances. That’s where the problems began:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;TASK&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Galaxy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;FTP&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;upload&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;******************************************************************************&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;jan&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-2019&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;-training-inst&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;FAILED!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;changed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;msg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;chown failed: failed to look up user galaxy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To understand what’s happened, have a look at the playbook:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;galaxyservers&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pre_tasks&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Create the Galaxy FTP upload dir&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/srv/galaxy/ftp&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;galaxy&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;galaxy&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0750&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;directory&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;galaxyproject.repos&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;galaxyproject.postgresql&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;natefoo.postgresql_objects&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;become&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;become_user&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;galaxyproject.galaxy&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;usegalaxy-eu.supervisor&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;geerlingguy.nginx&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;galaxyproject.proftpd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When building the playbook, we added the FTP dir creation &lt;code class=&quot;highlighter-rouge&quot;&gt;pre_task&lt;/code&gt; when adding the &lt;code class=&quot;highlighter-rouge&quot;&gt;galaxyproject.proftpd&lt;/code&gt; role (and
we added the roles in the order seen, running the playbook between each one). &lt;strong&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;galaxyproject.galaxy&lt;/code&gt; role creates
the &lt;code class=&quot;highlighter-rouge&quot;&gt;galaxy&lt;/code&gt; user&lt;/strong&gt;. So on a fresh target system, the &lt;code class=&quot;highlighter-rouge&quot;&gt;pre_task&lt;/code&gt; no longer works, since we now run it &lt;em&gt;before&lt;/em&gt; the role
that creates the user, when the first time around, we ran it &lt;em&gt;after&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Running it as a &lt;code class=&quot;highlighter-rouge&quot;&gt;post_task&lt;/code&gt; doesn’t work either. Galaxy starts when &lt;code class=&quot;highlighter-rouge&quot;&gt;usegalaxy-eu.supervisor&lt;/code&gt;’s handlers fire, at the
end of all the role executions, before any &lt;code class=&quot;highlighter-rouge&quot;&gt;tasks&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;post_tasks&lt;/code&gt; run. And when that happens, you get:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;RUNNING&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;HANDLER&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Restart&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Galaxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;*********************************************&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;fatal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;jan&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-2019&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;-training-inst&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;FAILED!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;changed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;msg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;galaxy: stopped&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;galaxy: ERROR (spawn error)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the error is:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ubuntu@jan-2019-training-inst-3:~$ sudo supervisorctl tail galaxy stderr
f2f0
python threads support enabled
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 306752 bytes (299 KB) for 4 cores
created farm 1 name: job-handlers mules:1,2
*** Operational MODE: threaded ***
added /srv/galaxy/server/lib/ to pythonpath.
DEBUG:galaxy.app:python path is: /srv/galaxy/server/lib/, ., , /srv/galaxy/venv/bin, /srv/galaxy/venv/lib/python2.7, /srv/galaxy/venv/lib/python2.7/plat-x86_64-linux-gnu, /srv/galaxy/venv/lib/python2.7/lib-tk, /srv/galaxy/venv/lib/python2.7/lib-old, /srv/galaxy/venv/lib/python2.7/lib-dynload, /usr/lib/python2.7, /usr/lib/python2.7/plat-x86_64-linux-gnu, /usr/lib/python2.7/lib-tk, /srv/galaxy/venv/local/lib/python2.7/site-packages, /srv/galaxy/venv/lib/python2.7/site-packages
DEBUG:galaxy.containers:config file './config/containers_conf.yml' does not exist, running with default config
Traceback (most recent call last):
  File &quot;/srv/galaxy/server/lib/galaxy/webapps/galaxy/buildapp.py&quot;, line 49, in app_factory
    app = galaxy.app.UniverseApplication(global_conf=global_conf, **kwargs)
  File &quot;/srv/galaxy/server/lib/galaxy/app.py&quot;, line 67, in __init__
    self.config.check()
  File &quot;/srv/galaxy/server/lib/galaxy/config.py&quot;, line 830, in check
    self._ensure_directory(path)
  File &quot;/srv/galaxy/server/lib/galaxy/config.py&quot;, line 815, in _ensure_directory
    raise ConfigurationError(&quot;Unable to create missing directory: %s\n%s&quot; % (path, e))
ConfigurationError: Unable to create missing directory: /srv/galaxy/ftp
[Errno 13] Permission denied: '/srv/galaxy/ftp'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re running Galaxy with privilege separation, so &lt;code class=&quot;highlighter-rouge&quot;&gt;/srv/galaxy&lt;/code&gt; is owned by root. This failure is the reason we added
the &lt;code class=&quot;highlighter-rouge&quot;&gt;pre_task&lt;/code&gt; in the first place.&lt;/p&gt;

&lt;p&gt;The solution in this case was relatively simple: &lt;code class=&quot;highlighter-rouge&quot;&gt;galaxyproject.proftpd&lt;/code&gt; has an option to create the FTP directory,
which I enabled. If not, however, this would be difficult to fix. There’s no way to insert tasks between roles unless
you run all of your roles from tasks using &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/include_role_module.html&quot;&gt;include_role&lt;/a&gt;, which is a hack. Assuming I don’t want to
modify roles I’ve fetched from &lt;a href=&quot;https://galaxy.ansible.com/&quot;&gt;Ansible Galaxy&lt;/a&gt; (no relation) to create the dir, the next best solution
would be to create a single-task role to run after &lt;code class=&quot;highlighter-rouge&quot;&gt;galaxyproject.galaxy&lt;/code&gt; to create the FTP directory. Not ideal.&lt;/p&gt;

&lt;h1 id=&quot;discussion&quot;&gt;Discussion&lt;/h1&gt;

&lt;p&gt;There are really two problems here, as far as I’m concerned.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Ansible doesn’t have a way to specify that tasks can fail &lt;em&gt;and should be retried at a later time&lt;/em&gt;. I actually came to
Ansible from CFEngine which (it’s been a few years, but as I recall) ran in &lt;em&gt;passes&lt;/em&gt; - actions it attempted could
fail, but it’d run other actions anyway and retry the failed ones later. Ansible has no such mechanism, its execution
is pretty much linear. The fact that you can’t insert a task between roles is really just an annoyance that makes it
difficult to work around this larger complaint.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I try to write the world’s most generalized and unopinionated roles, but Ansible does not make this easy. Creating
directories isn’t always as simple as you might think. Naïve roles will just use a &lt;code class=&quot;highlighter-rouge&quot;&gt;become&lt;/code&gt; or assume they are
running with the appropriate privileges, but neither of these are appropriate for a role that’s going to be used by
others, and could downright fail if the directory is on, for example, NFS with root squashing.  The
&lt;code class=&quot;highlighter-rouge&quot;&gt;galaxyproject.proftpd&lt;/code&gt;’s option to create the FTP directory is disabled by default for this reason (indeed, I myself
can’t use it for &lt;a href=&quot;https://usegalaxy.org/&quot;&gt;usegalaxy.org&lt;/a&gt;).  This is why I end up writing a lot of my own roles, and the reason
they end up so complex.&lt;/p&gt;

    &lt;p&gt;There is no construct in Ansible for a role to assert that it needs certain privileges, and for the consumer of
the role to configure the method by which those privileges should be obtained. You run a role as either the user you
logged in as, or with &lt;code class=&quot;highlighter-rouge&quot;&gt;become&lt;/code&gt;, but that’s too limiting. The &lt;a href=&quot;https://github.com/galaxyproject/ansible-galaxy.git&quot;&gt;galaxyproject.galaxy role&lt;/a&gt;, for example,
needs to create some files as one user and some as another, and execute commands or perform actions as both of those
users. If it can’t do that, you either have to:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Run everything as one user, this is less secure,&lt;/li&gt;
      &lt;li&gt;Use “enable/disable” variables and a bunch of &lt;code class=&quot;highlighter-rouge&quot;&gt;when&lt;/code&gt;s on a bunch of &lt;code class=&quot;highlighter-rouge&quot;&gt;include_tasks&lt;/code&gt;’s to break the role up by
privilege, then instruct role consumers on how to run the role multiple times with the correct combination of
&lt;code class=&quot;highlighter-rouge&quot;&gt;remote_user&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;become&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;become_user&lt;/code&gt; and the enable/disable vars set as role vars, or&lt;/li&gt;
      &lt;li&gt;Some kind of &lt;a href=&quot;https://github.com/galaxyproject/ansible-galaxy/pull/51&quot;&gt;nutty&lt;/a&gt; &lt;a href=&quot;https://github.com/galaxyproject/ansible-galaxy/pull/55&quot;&gt;kludgery&lt;/a&gt; to actually provide the missing assert/configure functionalty.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Somewhat related, but Ansible does not make it easy to see what privileges it’s obtaining and how it’s obtaining them.
  You can see most of this with &lt;code class=&quot;highlighter-rouge&quot;&gt;-vvv&lt;/code&gt; but it’s not easy: The ssh user is displayed on the first line after the task
  header, but only if &lt;code class=&quot;highlighter-rouge&quot;&gt;ansible_user&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;remote_user&lt;/code&gt; is set on some level. The &lt;code class=&quot;highlighter-rouge&quot;&gt;become_user&lt;/code&gt; is buried in the module
  execution command lines.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;My complaints in the discussion aside, the takeaway is that simply building a playbook that works is not sufficient, it
needs to be run again from scratch. In addition, any time you’re adding to it later, always consider whether the tasks
you’re adding depend on other things set up by the playbook. If they are, further consider whether those dependencies
run earlier or later in playbook. Forgetting this consideration is a trap I’ve fallen in to many times.&lt;/p&gt;</content><author><name>Nate Coraor</name></author><category term="ansible" /><category term="systems" /><summary type="html">The Mistake</summary></entry><entry><title type="html">Link Aggregations and Jumbo Frames in SmartOS</title><link href="https://nate.coraor.org/2017/11/09/smartos-aggr-mtu.html" rel="alternate" type="text/html" title="Link Aggregations and Jumbo Frames in SmartOS" /><published>2017-11-09T05:25:23+00:00</published><updated>2017-11-09T05:25:23+00:00</updated><id>https://nate.coraor.org/2017/11/09/smartos-aggr-mtu</id><content type="html" xml:base="https://nate.coraor.org/2017/11/09/smartos-aggr-mtu.html">&lt;h1 id=&quot;background&quot;&gt;Background&lt;/h1&gt;

&lt;p&gt;For a long while now I’ve been wanting to enable 802.3ad Link Aggregation (LACP) in conjunction with jumbo frames on my
&lt;a href=&quot;https://www.joyent.com/smartos&quot;&gt;SmartOS&lt;/a&gt; servers. This is a setup I’d used on Solaris 10, OpenSolaris, and Linux fileservers for years.
Unfortunately, due to SmartOS’ unique installation and boot paradigm (where the OS is not installed locally, it is
extracted into a ramdisk from the boot archive at boot), getting it set up was not so easy. In fact, back when I set up
my first SmartOS server in 2013, &lt;a href=&quot;https://github.com/joyent/smartos-live/issues/170&quot;&gt;it wasn’t possible&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That was rectified &lt;a href=&quot;https://wiki.smartos.org/display/DOC/Managing+NICs&quot;&gt;and documented&lt;/a&gt; (thanks &lt;a href=&quot;https://github.com/rmustacc&quot;&gt;@rmustacc&lt;/a&gt;!) a number of years ago,
but the one subsequent attmept to use it still ended in failure, so I gave up. Fast forward to yesterday, when I was
standing up my first new SmartOS server in a couple years and decided to give aggr+jumbos another shot.&lt;/p&gt;

&lt;h1 id=&quot;trying-it-out&quot;&gt;Trying it out&lt;/h1&gt;

&lt;p&gt;The documentation isn’t exactly clear on how to combine both options - aggregate interfaces are set up by hand in the
SmartOS &lt;code class=&quot;highlighter-rouge&quot;&gt;/usbkey/config&lt;/code&gt; config file, but MTUs are set using &lt;code class=&quot;highlighter-rouge&quot;&gt;nictagadm(1M)&lt;/code&gt;, which also appears to set up aggregate
interfaces. After looking through the contents of &lt;code class=&quot;highlighter-rouge&quot;&gt;nictagadm&lt;/code&gt; (it’s a shell script) and testing it out, it appears that
it’s just a handy tool for setting the MTU on the correct entities in &lt;code class=&quot;highlighter-rouge&quot;&gt;/usbkey/config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With the config in what was hopefully the correct state, I rebooted and prayed. Unfortunately, the network
(&lt;code class=&quot;highlighter-rouge&quot;&gt;svc:/network/physical:default&lt;/code&gt;) failed to start. Thankfully, SmartOS’s custom SMF method for the network is very
chatty:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh-session&quot;&gt;# cat /var/svc/log/network-physical:default.log
[ Nov  8 20:45:25 Executing start method (&quot;/lib/svc/method/net-physical&quot;). ]
[ Nov  8 20:45:25 Timeout override by svc.startd.  Using infinite timeout. ]
+ smf_configure_ip
+ /sbin/zonename -t
+ [ global = global -o shared = exclusive ]
+ return 0
+ LD_LIBRARY_PATH=/lib
+ export LD_LIBRARY_PATH
+ ADMIN_DHCP_TIMEOUT=300
+ ActiveAggrLinks=''
+ typeset -A ActiveAggrLinks
+ smf_netstrategy
+ smf_is_nonglobalzone
+ [ global != global ]
+ return 1
+ /sbin/netstrategy
+ set -- ufs none none
+ [ 0 -eq 0 ]
+ [ ufs = nfs ]
+ _INIT_NET_STRATEGY=none
+ export _INIT_NET_STRATEGY
+ typeset -A plumbedifs
+ smf_is_globalzone
+ [ global = global ]
+ return 0
+ /usr/sbin/dladm init-phys
+ log_if_state before
+ echo '== debug start: before =='
== debug start: before ==
+ /usr/sbin/dladm show-phys
LINK         MEDIA                STATE      SPEED  DUPLEX    DEVICE
bnx0         Ethernet             unknown    0      unknown   bnx0
bnx1         Ethernet             unknown    0      unknown   bnx1
+ /sbin/ifconfig -a
lo0: flags=2001000849&amp;lt;UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL&amp;gt; mtu 8232 index 1
        inet 127.0.0.1 netmask ff000000 
lo0: flags=2002000849&amp;lt;UP,LOOPBACK,RUNNING,MULTICAST,IPv6,VIRTUAL&amp;gt; mtu 8252 index 1
        inet6 ::1/128 
+ echo '== debug end: before =='
== debug end: before ==
+ load_sdc_sysinfo
+ boot_file_config_enabled
+ load_sdc_config
+ load_sdc_bootparams
+ sed -e 's/,/ /g'
+ echo ''
+ create_aggrs
+ typeset links macs mode mtu
+ [[ -z aggr0 ]]
+ aggrs=( aggr0 )
+ eval links='${SYSINFO_Aggregation_aggr0_Interfaces}'
+ links=bnx0,bnx1
+ eval macs='${SYSINFO_Aggregation_aggr0_MACs}'
+ macs=78:2b:cb:1b:06:9d,78:2b:cb:1b:06:9e
+ eval mode='${SYSINFO_Aggregation_aggr0_LACP_mode}'
+ mode=passive
+ eval mtu='${CONFIG_aggr0_mtu}'
+ mtu=9000
+ [[ -z off ]]
+ echo 'Creating aggr: aggr0 (mode=passive, links=bnx0,bnx1)'
Creating aggr: aggr0 (mode=passive, links=bnx0,bnx1)
+ dladm create-aggr -l bnx0 -l bnx1 -L passive aggr0
+ [[ 0 -eq 0 ]]
+ add_active_aggr_links aggr0 78:2b:cb:1b:06:9d,78:2b:cb:1b:06:9e
+ typeset alink
+ ActiveAggrLinks[78:2b:cb:1b:06:9d]=aggr0
+ ActiveAggrLinks[78:2b:cb:1b:06:9e]=aggr0
+ [[ -n 9000 ]]
+ dladm set-linkprop -p mtu=9000 aggr0
dladm: warning: cannot set link property 'mtu' on 'aggr0': operation not supported
+ [[ 1 -ne 0 ]]
+ echo 'Failed to set mtu on aggr aggr0 to 9000'
Failed to set mtu on aggr aggr0 to 9000
+ exit 95
[ Nov  8 20:45:35 Method &quot;start&quot; exited with status 95. ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What’s going on here?:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dladm: warning: cannot set link property 'mtu' on 'aggr0': operation not supported
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This was the very problem I’d had lo those many years ago, which, at the time, I’d attributed to a need to be able to
set the MTU on the individual interfaces before the aggegation, e.g.:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh-session&quot;&gt;# dladm set-linkprop -p mtu=9000 bnx0
# dladm set-linkprop -p mtu=9000 bnx1
# dladm create-aggr -l bnx0 -l bnx1 -L passive aggr0
# dladm set-linkprop -p mtu=9000 aggr0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Well, my conclusion as to the source of the error was completely wrong. As it turns out, the reason &lt;code class=&quot;highlighter-rouge&quot;&gt;set-linkprop&lt;/code&gt;
fails on the aggregation is because the Broadcom NetXtreme II &lt;code class=&quot;highlighter-rouge&quot;&gt;bnx&lt;/code&gt; driver doesn’t support runtime configuration of the
physical MTU limit. It can be set on IP interfaces, but not with &lt;code class=&quot;highlighter-rouge&quot;&gt;dladm set-linkprop&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;configuring-bnx&quot;&gt;Configuring bnx&lt;/h1&gt;

&lt;p&gt;The MTU must be configured through the &lt;code class=&quot;highlighter-rouge&quot;&gt;bnx&lt;/code&gt; driver’s config file, &lt;code class=&quot;highlighter-rouge&quot;&gt;bnx.conf&lt;/code&gt;. The version that ships with the OS can
be found in &lt;code class=&quot;highlighter-rouge&quot;&gt;/kernel/drv/bnx.conf&lt;/code&gt;. Some sources online and the &lt;a href=&quot;https://docs.oracle.com/cd/E86824_01/html/E54777/bnx-7d.html&quot;&gt;Solaris 11 docs&lt;/a&gt; claim you need to set
the &lt;code class=&quot;highlighter-rouge&quot;&gt;Jumbo=...&lt;/code&gt; option, but in the case of the driver version currently shipping in illumos, the option name is &lt;code class=&quot;highlighter-rouge&quot;&gt;mtu&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But this is SmartOS, I can’t just change &lt;code class=&quot;highlighter-rouge&quot;&gt;bnx.conf&lt;/code&gt; and reboot like I would with traditional Solaris. Thankfully, I
stumbled across &lt;a href=&quot;http://blog.shalman.org/overriding-driver-config-files-on-smartos/&quot;&gt;a solution&lt;/a&gt; posted by &lt;a href=&quot;https://github.com/nshalman&quot;&gt;@nshalman&lt;/a&gt;. This clever enhancement by
&lt;a href=&quot;https://github.com/wesolows&quot;&gt;@wesolows&lt;/a&gt; and &lt;a href=&quot;https://github.com/JohnSonnenschein&quot;&gt;@JohnSonnenschein&lt;/a&gt; allows arbitrary files to be added to the
boot archive ramdisk via the bootloader. As @nshalman says, @wesolows’ &lt;a href=&quot;http://dtrace.org/blogs/wesolows/2013/12/28/anonymous-tracing-on-smartos/&quot;&gt;blog post&lt;/a&gt; where
it’s mentioned is pretty enlightening, as is the &lt;a href=&quot;https://github.com/wesolows/illumos-joyent/blob/6f4b830de6b4427d63fa12598c41301feebc4a42/README&quot;&gt;“module” syntax documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My hosts boot from PXE, and use iPXE as their bootloader. Thankfully, iPXE’s &lt;a href=&quot;http://ipxe.org/cmd/imgfetch&quot;&gt;module (imgfetch)&lt;/a&gt; command
works in exactly the same way as the GRUB equivalent, and it pulls the file off my TFTP server just like the rest of
the boot components. Over on my (Debian) boot server, I now have:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# cat /var/lib/tftpboot/smartos/bnx.conf
mtu=9000,9000,9000,9000,9000,9000,9000,9000,9000,9000,9000,9000,9000,9000,9000,9000;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And my &lt;code class=&quot;highlighter-rouge&quot;&gt;menu.ipxe&lt;/code&gt; entry for booting SmartOS has changed from:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kernel /smartos/${smartos-version}/platform/i86pc/kernel/amd64/unix -B smartos=true,console=${smartos-console},${smartos-console}-mode=&quot;115200,8,n,1,-&quot; -v
initrd /smartos/${smartos-version}/platform/i86pc/amd64/boot_archive
boot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kernel /smartos/${smartos-version}/platform/i86pc/kernel/amd64/unix -B smartos=true,console=${smartos-console},${smartos-console}-mode=&quot;115200,8,n,1,-&quot; -v
module /smartos/${smartos-version}/platform/i86pc/amd64/boot_archive type=rootfs name=ramdisk
module /smartos/bnx.conf type=file name=kernel/drv/bnx.conf
boot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Regarding the &lt;code class=&quot;highlighter-rouge&quot;&gt;name&lt;/code&gt; argument to the &lt;code class=&quot;highlighter-rouge&quot;&gt;type=rootfs&lt;/code&gt; module, the &lt;a href=&quot;https://github.com/wesolows/illumos-joyent/blob/6f4b830de6b4427d63fa12598c41301feebc4a42/README&quot;&gt;documentation&lt;/a&gt; claims “&lt;code class=&quot;highlighter-rouge&quot;&gt;This
option is ignored for rootfs modules&lt;/code&gt;”. However, both @wesolows and @nshalman’s examples use it, and I didn’t test
without it, so I left it alone.&lt;/p&gt;

&lt;p&gt;Reboot, and you should find that &lt;code class=&quot;highlighter-rouge&quot;&gt;bnx.conf&lt;/code&gt; has been copied to &lt;code class=&quot;highlighter-rouge&quot;&gt;/system/boot/kernel/drv/bnx.conf&lt;/code&gt;. At first I was
looking for it in &lt;code class=&quot;highlighter-rouge&quot;&gt;/kernel/drv/bnx.conf&lt;/code&gt;, and thought it must not be working, but the
&lt;a href=&quot;https://github.com/wesolows/illumos-joyent/blob/6f4b830de6b4427d63fa12598c41301feebc4a42/README&quot;&gt;documentation&lt;/a&gt; clears this point up.&lt;/p&gt;

&lt;p&gt;I also thought it wasn’t working because even with &lt;code class=&quot;highlighter-rouge&quot;&gt;bnx.conf&lt;/code&gt; in place and disabling all network configuration in
&lt;code class=&quot;highlighter-rouge&quot;&gt;/usbkey/config&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;dladm set-linkprop -p mtu=9000 bnx0&lt;/code&gt; was still failing. It turns out that this is normal for the
&lt;code class=&quot;highlighter-rouge&quot;&gt;bnx&lt;/code&gt; driver. The MTU cannot be set via &lt;code class=&quot;highlighter-rouge&quot;&gt;set-linkprop&lt;/code&gt;, it must be done through &lt;code class=&quot;highlighter-rouge&quot;&gt;bnx.conf&lt;/code&gt;. Thankfully, once you have
the aggregation configured correctly, the call to &lt;code class=&quot;highlighter-rouge&quot;&gt;dladm set-linkprop -p mtu=9000 aggr0&lt;/code&gt; in
&lt;code class=&quot;highlighter-rouge&quot;&gt;/lib/svc/method/net-physical&lt;/code&gt; does not fail.&lt;/p&gt;

&lt;p&gt;Here is my working &lt;code class=&quot;highlighter-rouge&quot;&gt;/usbkey/config&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# This file was auto-generated and must be source-able by bash.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;aggr0_aggr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;78:2b:cb:1b:06:9d,78:2b:cb:1b:6:9e
&lt;span class=&quot;nv&quot;&gt;aggr0_lacp_mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;passive
&lt;span class=&quot;nv&quot;&gt;aggr0_mtu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;9000

&lt;span class=&quot;nv&quot;&gt;admin_nic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;aggr0
&lt;span class=&quot;nv&quot;&gt;admin_mtu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;9000
&lt;span class=&quot;nv&quot;&gt;admin_ip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;172.18.2.13
&lt;span class=&quot;nv&quot;&gt;admin_netmask&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;255.255.255.0
&lt;span class=&quot;nv&quot;&gt;admin_gateway&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;172.18.2.1

&lt;span class=&quot;nv&quot;&gt;external_nic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;aggr0
&lt;span class=&quot;nv&quot;&gt;external_mtu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;9000
&lt;span class=&quot;nv&quot;&gt;external0_ip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;128.118.250.13
&lt;span class=&quot;nv&quot;&gt;external0_netmask&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;255.255.255.224
&lt;span class=&quot;nv&quot;&gt;external0_gateway&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;128.118.250.1
&lt;span class=&quot;nv&quot;&gt;external0_vlan_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;306

&lt;span class=&quot;nv&quot;&gt;headnode_default_gateway&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;128.118.250.1

&lt;span class=&quot;nv&quot;&gt;dns_resolvers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;128.118.250.8,54.172.110.33
&lt;span class=&quot;nv&quot;&gt;dns_domain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;galaxyproject.org

&lt;span class=&quot;nv&quot;&gt;ntp_hosts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.smartos.pool.ntp.org
&lt;span class=&quot;nv&quot;&gt;compute_node_ntp_hosts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dhcp

&lt;span class=&quot;nv&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;westmalle.galaxyproject.org
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;The main problem here seemed to be (other than my confusion at various points) the bnx driver. Many people report
having an overall better experience with Intel cards, which I do not doubt.&lt;/p&gt;

&lt;p&gt;So, I’m happy to report that LACP + jumbo frames are working great (and probably have been for years), as long as you
can thwack your network device’s driver in to submission.&lt;/p&gt;

&lt;p&gt;Thanks as always to the Joyeurs for their hard work and support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Addendum 2017-11-09 11:51:00 -0400:&lt;/strong&gt; I’ve disabled &lt;code class=&quot;highlighter-rouge&quot;&gt;aggr0_lacp_mode&lt;/code&gt; and set my switch to use non-LACP trunking.
Since I boot from PXE, and because LACP is not activated until the OS is booted, PXE was unable to contact the network
to boot. Multilink trunking/failover still works and I wasn’t using dynamic LACP anyway, so this is not a huge issue.&lt;/p&gt;</content><author><name>Nate Coraor</name></author><category term="smartos" /><category term="systems" /><summary type="html">Background</summary></entry><entry><title type="html">Debian root in a btrfs subvolume</title><link href="https://nate.coraor.org/2015/11/04/debian-root-in-a-btrfs-subvolume.html" rel="alternate" type="text/html" title="Debian root in a btrfs subvolume" /><published>2015-11-04T15:09:49+00:00</published><updated>2015-11-04T15:09:49+00:00</updated><id>https://nate.coraor.org/2015/11/04/debian-root-in-a-btrfs-subvolume</id><content type="html" xml:base="https://nate.coraor.org/2015/11/04/debian-root-in-a-btrfs-subvolume.html">&lt;h1 id=&quot;background&quot;&gt;Background&lt;/h1&gt;

&lt;p&gt;If you select btrfs as your filesystem of choice in the Ubuntu 14.04 installer,
it’ll create &lt;a href=&quot;https://btrfs.wiki.kernel.org/index.php/SysadminGuide#Subvolumes&quot;&gt;btrfs
subvolumes&lt;/a&gt;
for any “partitions” you request. These partitions will be subvolumes named &lt;code class=&quot;highlighter-rouge&quot;&gt;@&lt;/code&gt;
for &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;@home&lt;/code&gt; for &lt;code class=&quot;highlighter-rouge&quot;&gt;/home&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Sadly, while the Debian installer will install into btrfs, it will not use
subvolumes. Thus, after installing Debian, if you want to get your root
filesystem into a subvolume, you have to do a bit of work. I wanted to do this
because it makes things like snapshots much cleaner when the OS is not
installed directly in the btrfs root (otherwise you can’t have any space in
btrfs “outside” the mounted filesystem).&lt;/p&gt;

&lt;h1 id=&quot;process&quot;&gt;Process&lt;/h1&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Install Debian as normal.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Once installed, I like to immediately create a snapshot of the installed
system:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# btrfs subvol snap -r / /@stretch_fcs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Create the snapshot that will become the new root fs:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# btrfs subvol snap /@stretch_fcs /@stretch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Update grub config in the snapshot to refer to the subvolume (&lt;a href=&quot;http://unix.stackexchange.com/questions/62802/move-a-linux-instalation-using-btrfs-on-the-default-subvolume-subvolid-0-to-an&quot;&gt;thanks
goncalopp@stackexchange&lt;/a&gt;).
My procedure is slightly different since I’m using EFI boot (mounting
&lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi&lt;/code&gt; in the chroot is probably not even necessary, but, just in
case). If your /boot is a separate partition, you would still need to mount
&lt;code class=&quot;highlighter-rouge&quot;&gt;/boot&lt;/code&gt; before &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# btrfs mount -o subvol=@stretch /mnt
# cd /mnt
# mount -o bind /dev dev
# mount -o bind /sys sys
# mount -o bind /proc proc
# mount -o bind /boot/efi boot/efi
# chroot .
# update-grub
# exit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Update the subvolume’s &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/fstab&lt;/code&gt; to mount the subvolume as the root
filesystem. Excuse the lazy regexes, you probably want to just edit
&lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/fstab&lt;/code&gt; by hand, but I don’t like putting “edit this file” in
instructions):&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# grep '^[^#].* / ' /etc/fstab
UUID=71d1dc27-36d5-492b-b51c-f852fdaa6b4e /               btrfs   defaults        0       1
# sed -ie '/ \/ /s#defaults\([^ ]*\) *#defaults\1,subvol=@stretch #' /tmp/fstab
# grep '^[^#].* / ' /etc/fstab
UUID=71d1dc27-36d5-492b-b51c-f852fdaa6b4e /               btrfs   defaults,subvol=@stretch 0       1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Set the default btrfs filesystem to the &lt;code class=&quot;highlighter-rouge&quot;&gt;@stretch&lt;/code&gt; snapshot. We will undo
this later because leaving the subvolume as the default means that it’s more
cumbersome to mount the btrfs root. However, if we were to reboot now
without changing the default, Grub would boot using &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/grub/grub.cfg&lt;/code&gt;
rather than &lt;code class=&quot;highlighter-rouge&quot;&gt;/@stretch/boot/grub/grub.cfg&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; would be the mounted
filesystem once booted. (Aside, &lt;code class=&quot;highlighter-rouge&quot;&gt;btrfs&lt;/code&gt; can’t select a subvolume by name and
just print its id? Annoying):&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# btrfs subvol set-default $(btrfs subvol list / | awk '$NF == &quot;@stretch&quot; { print $2 }') /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Reboot:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Verify that you have booted with &lt;code class=&quot;highlighter-rouge&quot;&gt;@stretch&lt;/code&gt; as the root filesystem:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/
	Name: 			@stretch
	uuid: 			284d7ea5-f406-2541-953f-60772fc247af
	Parent uuid: 		09184ee6-f5f9-5a4c-9d1b-2557b0541f0a
	Creation time: 		2015-11-04 10:04:10
	Object ID: 		282
	Generation (Gen): 	502
	Gen at creation: 	482
	Parent: 		5
	Top Level: 		5
	Flags: 			-
	Snapshot(s):
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Reset &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; as the default subvolume (the btrfs root’s subvolume id is always 5):&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# btrfs subvol set-default 5 /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Reinstall grub so that it knows to read its config from
&lt;code class=&quot;highlighter-rouge&quot;&gt;/@stretch/boot/grub/grub.cfg&lt;/code&gt; (assuming your boot device is &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/sda&lt;/code&gt;):&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# update-grub
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.2.0-1-amd64
Found initrd image: /boot/initrd.img-4.2.0-1-amd64
done
# update-grub /dev/sda
Installing for x86_64-efi platform.
Installation finished. No error reported.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;Reboot:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# reboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;You should now be able to mount &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/sda3&lt;/code&gt; (or whatever your btrfs device
is) and see the entire tree. I just put it in /etc/fstab and keep it
mounted at /btrfs:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# grep /btrfs /etc/fstab
UUID=71d1dc27-36d5-492b-b51c-f852fdaa6b4e /btrfs          btrfs   defaults      0       1
# mkdir /btrfs &amp;amp;&amp;amp; mount /btrfs
# ls -l /btrfs
bin   dev  home        lib    media  opt   root  sbin  @stretch      sys  usr  vmlinuz
boot  etc  initrd.img  lib64  mnt    proc  run   srv   @stretch_fcs  tmp  var
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;If you want, you can now remove &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;btrfs_root&amp;gt;/&amp;lt;non-subvol-contents&amp;gt;&lt;/code&gt; e.g.:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# cd /btrfs
# rm -rf bin boot dev etc home initrd.img lib lib64 media mnt opt proc root run sbin srv sys tmp usr var vmlinuz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now &lt;code class=&quot;highlighter-rouge&quot;&gt;/btrfs&lt;/code&gt; is free for you to create things in, e.g. a &lt;code class=&quot;highlighter-rouge&quot;&gt;snapshots/&lt;/code&gt; directory
in which you can create your btrfs snapshots.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;h1 id=&quot;todo&quot;&gt;TODO&lt;/h1&gt;

&lt;p&gt;My desktop originally ran Ubuntu 14.04. I later wanted to switch to Debian
Stretch without wiping out Ubuntu. I can now dual boot between them thanks to
btrfs subvolumes and UEFI. So I need to document that…&lt;/p&gt;</content><author><name>Nate Coraor</name></author><category term="debian" /><category term="installation" /><summary type="html">Background</summary></entry><entry><title type="html">Python Packaging: What’s the Big Wheel?</title><link href="https://nate.coraor.org/2015/08/28/python-packaging-whats-the-big-wheel.html" rel="alternate" type="text/html" title="Python Packaging: What's the Big Wheel?" /><published>2015-08-28T20:03:10+00:00</published><updated>2015-08-28T20:03:10+00:00</updated><id>https://nate.coraor.org/2015/08/28/python-packaging-whats-the-big-wheel</id><content type="html" xml:base="https://nate.coraor.org/2015/08/28/python-packaging-whats-the-big-wheel.html">&lt;h1 id=&quot;background&quot;&gt;Background&lt;/h1&gt;

&lt;p&gt;Python modules have numerous binary packaging formats, but the one that’s
emerged as the winner has been the &lt;a href=&quot;https://wheel.readthedocs.org/&quot;&gt;Wheel
format&lt;/a&gt;. Wheel comes with a few improvements
over Python’s previous binary packaging format, Eggs.  Unfortunately, it does
not solve the largest problem that &lt;a href=&quot;https://galaxyproject.org/&quot;&gt;we&lt;/a&gt; with eggs.
&lt;a href=&quot;http://lucumr.pocoo.org/2014/1/27/python-on-wheels/&quot;&gt;Armin Ronacher explains this quite
well&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There are a few problems with wheels however. One of the problems is that
wheels inherit some of the problems that egg already had. For instance Linux
binary distributions are still not an option for most people because of two
basic problems: Python itself being compiled in different forms on Linux and
modules being linked against different system libraries. The first problem is
caused by Python 2 coming in two flavours that are both incompatible to each
other: UCS2 Pythons and UCS4 Pythons. Depending on which mode Python is
compiled with the ABI looks different. Presently the wheel format (from what
I can tell) does not annotate for which Python unicode mode a library is
linked. A separate problem is that Linux distributions are less compatible to
each other as you would wish and concerns have been brought up that wheels
compiled on one distribution will not work on others.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;The end effect of this is that you presently cannot upload binary wheels to
PyPI on concerns of incompatibility with different setups.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;http://dev.list.galaxyproject.org/Question-Regarding-fetch-eggs-amp-c-vs-pip-td4666564.html#a4666567&quot;&gt;Here’s why that’s such a big problem for
Galaxy&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Galaxy has a huge (and ever-growing) list of dependent python modules with C
extensions. If we did not prebuild and distribute eggs for these, the initial
setup to get Galaxy running would be long and problematic. Some people who
download Galaxy to develop tools may not even have compilers installed, let
alone the multitude of -dev or -devel packages that aren’t part of a default
Debian or RHEL installation that would be required to build all of these
packages from source. One of the things that I feel makes Galaxy so
accessible is that you can start using it immediately after you clone the
source. So that ability to clone and start and have it work as reliably (and
quickly) as possible is a high priority.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mac OS X wheels are allowed in PyPI, and that’s great as a lot of development
happens on OS X, but almost all production Galaxy servers run on Linux, and for
this, we still have no solution.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mail.python.org/pipermail/distutils-sig/2010-January/015345.html&quot;&gt;For a long time in
Galaxy&lt;/a&gt;,
we’ve monkeypatched setuptools’ platform detection to add the necessary
UCS2/UCS4 detection and left it at that, hoping a better solution would come
along. When Wheels emerged as the new packaging format but did not solve these
problems, I decided to put in the work to solve them.&lt;/p&gt;

&lt;h1 id=&quot;preliminary-work-and-discussion&quot;&gt;Preliminary Work and Discussion&lt;/h1&gt;

&lt;p&gt;This work began as some discussions with
&lt;a href=&quot;http://pypaio.readthedocs.org/en/latest/&quot;&gt;PyPA&lt;/a&gt; people on IRC but after making
&lt;a href=&quot;https://bitbucket.org/pypa/wheel/pull-requests/54/soabi-2x-platform-os-distro-support-for/diff&quot;&gt;a quick
implementation&lt;/a&gt;
that turned out to be too naive, &lt;a href=&quot;http://code.activestate.com/lists/python-distutils-sig/26009/&quot;&gt;I opened a
discussion&lt;/a&gt; with
the
&lt;a href=&quot;https://www.python.org/community/sigs/current/distutils-sig/&quot;&gt;Distutils-SIG&lt;/a&gt;
to figure out where to go with it.&lt;/p&gt;

&lt;h2 id=&quot;a-good-tangent&quot;&gt;A Good Tangent&lt;/h2&gt;

&lt;p&gt;This discussion quickly grew in scope to discuss another failing of wheel, in
that it does not provide a mechanism for dealing with &lt;em&gt;externally linked
dependencies&lt;/em&gt;. My canonical example here is the
&lt;a href=&quot;http://initd.org/psycopg/&quot;&gt;psycopg2&lt;/a&gt; Python module, the most popular DB-API
implementation for PostgreSQL. It depends on the libpq (PostgreSQL’s C API)
shared library being available at runtime. So even if we do build and host a
psycopg2 wheel, anyone wanting to use it has to install whatever package
provides (or compile from source) &lt;code class=&quot;highlighter-rouge&quot;&gt;libpq.so&lt;/code&gt; on their system and ensure it’s on
the runtime linker path, or else psycopg2 will fail to import. Wheels provide
no mechanism to inform what system libraries, packages, or anything else that’s
external what they depend on. A variety of solutions were proposed, check out
the thread if you’d like to see some of the very good thinking on this subject.&lt;/p&gt;

&lt;p&gt;Of course, this is a &lt;em&gt;general&lt;/em&gt; wheel problem, not just wheels on Linux - so if
a psycopg2 wheel for OS X was in PyPI today, and you installed it but didn’t
have libpq installed, you’d run into the same problem you’d have on Linux.&lt;/p&gt;

&lt;p&gt;This problem interests me (for example, to solve it for Galaxy’s eggs, we
compile a static version of libpq and link that into &lt;a href=&quot;http://eggs.galaxyproject.org/psycopg2/&quot;&gt;the
egg&lt;/a&gt;), but it isn’t the problem I’m
trying to solve (and indeed, if you follow the discussion far enough, it
eventually finds &lt;a href=&quot;http://code.activestate.com/lists/python-distutils-sig/26056/&quot;&gt;its own
thread&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;the-state-of-things&quot;&gt;The State of Things&lt;/h2&gt;

&lt;p&gt;Solving the external dependency problem is good and worth doing, but for the
moment I want to improve these problems in the current state:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Linux wheels are not allowed in PyPI.&lt;/li&gt;
  &lt;li&gt;Compiling a package from a source distribution requires installation of a
variety of development tools and header packages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Figuring out the right mix of packages may require multiple iterations of
attempted package installs, with errors that may be fairly confusing for people
not used to compiling software:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# pip install psycopg2
Collecting psycopg2
  Using cached psycopg2-2.6.1.tar.gz
Installing collected packages: psycopg2
  Running setup.py install for psycopg2
    Complete output from command /usr/bin/python -c &quot;import setuptools, tokenize;__file__='/tmp/pip-build-C8JPB7/psycopg2/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))&quot; install --record /tmp/pip-UIFRep-record/install-record.txt --single-version-externally-managed --compile:
    running install
    running build
      ...
    running build_ext
    building 'psycopg2._psycopg' extension
    creating build/temp.linux-x86_64-2.7
    creating build/temp.linux-x86_64-2.7/psycopg
    x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -DPSYCOPG_DEFAULT_PYDATETIME=1 -DPSYCOPG_VERSION=&quot;2.6.1 (dt dec pq3 ext lo64)&quot; -DPG_VERSION_HEX=0x090403 -DHAVE_LO64=1 -I/usr/include/python2.7 -I. -I/usr/include/postgresql -I/usr/include/postgresql/9.4/server -c psycopg/psycopgmodule.c -o build/temp.linux-x86_64-2.7/psycopg/psycopgmodule.o -Wdeclaration-after-statement
    In file included from psycopg/psycopgmodule.c:27:0:
    ./psycopg/psycopg.h:30:20: fatal error: Python.h: No such file or directory
     #include &amp;lt;Python.h&amp;gt;
                        ^
    compilation terminated.
    error: command 'x86_64-linux-gnu-gcc' failed with exit status 1
    
    ----------------------------------------
Command &quot;/usr/bin/python -c &quot;import setuptools, tokenize;__file__='/tmp/pip-build-C8JPB7/psycopg2/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))&quot; install --record /tmp/pip-UIFRep-record/install-record.txt --single-version-externally-managed --compile&quot; failed with error code 1 in /tmp/pip-build-C8JPB7/psycopg2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Granted, the error you will receive when you install a wheel without the
external dependencies installed could also be confusing for people new to *NIX:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Traceback (most recent call last):
  File &quot;&amp;lt;string&amp;gt;&quot;, line 1, in &amp;lt;module&amp;gt;
  File &quot;/usr/local/lib/python2.7/dist-packages/psycopg2/__init__.py&quot;, line 50, in &amp;lt;module&amp;gt;
    from psycopg2._psycopg import BINARY, NUMBER, STRING, DATETIME, ROWID
ImportError: libpq.so.5: cannot open shared object file: No such file or directory
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But I argue that that’s at least better than the previous situation - plus,
package authors can always catch that &lt;code class=&quot;highlighter-rouge&quot;&gt;ImportError&lt;/code&gt; and provide an error
message with a bit more guidance. But the big point is that you only need to
have one package installed, no compilers, and no -dev packages. If you are
installing psycopg2 there’s a decent likelihood you have the &lt;code class=&quot;highlighter-rouge&quot;&gt;libpq&lt;/code&gt; package
(on Debian) installed, but not as much of a likelihood you have &lt;code class=&quot;highlighter-rouge&quot;&gt;libpq-dev&lt;/code&gt;
installed.&lt;/p&gt;

&lt;p&gt;And one minor point, all of this is potentially on a production box. These
days, especially with Docker, I don’t even like to install compilers on
production boxes if I don’t have to.&lt;/p&gt;

&lt;h2 id=&quot;direction&quot;&gt;Direction&lt;/h2&gt;

&lt;p&gt;I ultimately declined my own &lt;a href=&quot;https://bitbucket.org/pypa/wheel/pull-requests/54/soabi-2x-platform-os-distro-support-for/diff&quot;&gt;pull request on
wheel&lt;/a&gt;
because the Linux platform detection was flawed. As a result, there’s a new
library in my versions of pip and wheel that attempts to detect the current
linux distribution in a variety of ways, but preferably via &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/os-release&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As it turns out, the use of &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/os-release&lt;/code&gt; for platform detection had
&lt;a href=&quot;http://code.activestate.com/lists/python-distutils-sig/24584/&quot;&gt;already been hashed out on Distutils-SIG a while
ago&lt;/a&gt;, but I
didn’t discover that thread until today. Thankfully, it looks like my
implementation should go along with the proposal discussed there (my
implementation was based on Nick Coghlan’s suggestion in my later thread, so
it’s no surprise).&lt;/p&gt;

&lt;p&gt;Eventually I’d like to have it do illumos distribution detection as well, but
that’s a much lower priority.&lt;/p&gt;

&lt;h1 id=&quot;changes&quot;&gt;Changes&lt;/h1&gt;

&lt;h2 id=&quot;abi-tags&quot;&gt;ABI Tags&lt;/h2&gt;

&lt;p&gt;The first problem was that of Python ABI compatibility with differing Unicode
widths. Thankfully, &lt;a href=&quot;https://www.python.org/dev/peps/pep-0425/&quot;&gt;PEP 425&lt;/a&gt;
provides the specification for how UCS and other ABI incompatibilities should
be tagged in built distributions such as wheel. Unfortunately, the &lt;a href=&quot;https://www.python.org/dev/peps/pep-0425/#abi-tag&quot;&gt;ABI
tag&lt;/a&gt; was not implemented for
Python 2 (see related issues,
&lt;a href=&quot;https://bitbucket.org/pypa/pypi-metadata-formats/issues/25&quot;&gt;pypi-metadata-format#25&lt;/a&gt;,
&lt;a href=&quot;https://bitbucket.org/pypa/wheel/issues/101/python-wheels-need-ucs-tag-on-2x&quot;&gt;wheel#101&lt;/a&gt;,
and
&lt;a href=&quot;https://bitbucket.org/pypa/wheel/issues/63/backport-soabi-to-python-2&quot;&gt;wheel#63&lt;/a&gt;).
However, implementing this tag in the pip and wheel packages was relatively
easy.&lt;/p&gt;

&lt;h2 id=&quot;platform-tags&quot;&gt;Platform Tags&lt;/h2&gt;

&lt;p&gt;If the platform tag doesn’t start with Linux, there’s no changes here. On
Linux, I append the Linux distribution and distribution version number to the
platform portion of the tag. So where the old tag would’ve been &lt;code class=&quot;highlighter-rouge&quot;&gt;linux_x86_64&lt;/code&gt;
on a 64-bit x86 CPU, the new tag will read like &lt;code class=&quot;highlighter-rouge&quot;&gt;linux_x86_64_ubuntu_14_04&lt;/code&gt; or
&lt;code class=&quot;highlighter-rouge&quot;&gt;linux_x86_64_debian_8&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thankfully, the PEP 425 reference implementation was written with tag
specificity in mind, so in fact when comparing tags, a list of tags, from most
to least specific, is checked for compatibility. In the case of the platform
tag:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;When &lt;strong&gt;installing&lt;/strong&gt; wheels, the wheel with the most-specific matching tag
will be installed first. A list of platform tags might look like:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;linux_x86_64_rhel_6_5&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;linux_x86_64_rhel_6&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;linux_x86_64&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;any&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;When &lt;strong&gt;building&lt;/strong&gt; wheels, the tag for the distribution’s &lt;em&gt;major&lt;/em&gt; version will
be preferred, for example, &lt;code class=&quot;highlighter-rouge&quot;&gt;linux_x86_64_rhel_6&lt;/code&gt;. It is possible to build a
wheel for the specific major/minor version using the &lt;code class=&quot;highlighter-rouge&quot;&gt;--plat-name&lt;/code&gt; argument
to &lt;code class=&quot;highlighter-rouge&quot;&gt;bdist_wheel&lt;/code&gt; if the wheel builder has reason to believe it would not be
compatible with older minor releases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See related issue &lt;a href=&quot;https://bitbucket.org/pypa/pypi-metadata-formats/issues/15&quot;&gt;pypi-metadata-formats#15&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;binary-compatibilitycfg&quot;&gt;binary-compatibility.cfg&lt;/h2&gt;

&lt;p&gt;As you may have noticed, it’s now possible to build wheels that are targeted
for RHEL, but what if you don’t have a RHEL system on which to build, but you
do have a CentOS system. Or what if someone built RHEL wheels and you want to
install them on your CentOS system. Of course, because they are ABI compatible,
it shouldn’t be a problem to install one’s wheels on the other. As a solution
for this, Nick Coghlan proposed a file, &lt;code class=&quot;highlighter-rouge&quot;&gt;binary-compatibility.cfg&lt;/code&gt;, in JSON
format, to be found in either &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/python&lt;/code&gt; or the root of a currently active
virtualenv, which defines what platforms the current platform is compatible
with. So for example, to build and install RHEL 7 wheels on my CentOS 7 host,
I’d write a &lt;code class=&quot;highlighter-rouge&quot;&gt;binary-compatibility.cfg&lt;/code&gt; like so:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;linux_x86_64_centos_7&quot;: {
    &quot;build&quot;: &quot;linux_x86_64_rhel_7&quot;,
    &quot;install&quot;: [&quot;linux_x86_64_rhel_7&quot;]
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ideally, &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/python/binary-compatibility.cfg&lt;/code&gt; would be maintained by
distribution vendors.&lt;/p&gt;

&lt;h2 id=&quot;generic-wheels&quot;&gt;“Generic” wheels&lt;/h2&gt;

&lt;p&gt;As mentioned above, it is possible to override the platform tag using the
&lt;code class=&quot;highlighter-rouge&quot;&gt;--plat-name&lt;/code&gt; argument to &lt;code class=&quot;highlighter-rouge&quot;&gt;bdist_wheel&lt;/code&gt;. Because the &lt;code class=&quot;highlighter-rouge&quot;&gt;linux_x86_64&lt;/code&gt; platform
remains in the list of platforms for which a wheel will be searched, a wheel
builder can choose to build for that platform if it has no non-standard
external dependencies.&lt;/p&gt;

&lt;p&gt;Anyone execising this option should take care that:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;No non-standard libraries (anything other than libc, libm, etc.) are linked
by &lt;em&gt;any&lt;/em&gt; dynamic object in the built distribution.&lt;/li&gt;
  &lt;li&gt;The wheel is built on a suitably old platform that it will run on any Linux
release within the last ~5 years. Due to symbol versioning in the GNU C
Library (glibc), modules built on a newer glibc cannot be used with an older
glibc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ideally, both of these checks could be automated, and perhaps the decision
could be made to utilize the “generic” tag automatically if both 1 and 2 are
true, except that “suitably old” glibc versions would need to be a policy
decision and updated on a semi-regular basis.&lt;/p&gt;

&lt;h2 id=&quot;abi-stability&quot;&gt;ABI stability&lt;/h2&gt;

&lt;p&gt;The platform detection code currently makes a rudimentary effort to determine
whether a given Linux distribution and version can be considered to have a
stable ABI or not. This information is not used, but may be useful for PyPI to
decide whether or not to accept wheels for a given platform tag.&lt;/p&gt;

&lt;h1 id=&quot;supporting-work&quot;&gt;Supporting work&lt;/h1&gt;

&lt;h2 id=&quot;docker-build&quot;&gt;docker-build&lt;/h2&gt;

&lt;p&gt;I don’t have time to write much about it here, but this work was tested using
&lt;a href=&quot;https://github.com/galaxyproject/docker-build/&quot;&gt;Galaxy docker-build&lt;/a&gt;, which
received quite a bit of work over the process itself. docker-build is a set of
scripts and YAML that we use to build Galaxy tool dependencies (typically
scientific software) in Docker, for packaging in the Galaxy Tool Shed.&lt;/p&gt;

&lt;p&gt;I extended docker-build to build wheels for as many distributions as had
official images in Docker Hub, plus a few more, for both the UCS-2, and UCS-4
variants of Python 2.6 and 2.7. Note to self, I need to push updated *-wheel
images to Docker Hub.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;I hope this is accepted upstream, although I haven’t seemed to attract much
attention with my latest posts to Distutils-SIG. I may try opening PRs shortly
and see if that spawns any discussion.&lt;/p&gt;

&lt;p&gt;The modifications to wheel are in &lt;a href=&quot;https://bitbucket.org/natefoo/wheel&quot;&gt;my wheel repository on
Bitbucket&lt;/a&gt;. The modifications to pip are
in &lt;a href=&quot;https://github.com/natefoo/pip/tree/linux-wheels&quot;&gt;the linux-wheels branch of my pip repository on
GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can see the wheels that I’ve built using docker-build &lt;a href=&quot;https://wheels.galaxyproject.org/&quot;&gt;on our wheel
index&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nate Coraor</name></author><category term="python" /><category term="packaging" /><summary type="html">Background</summary></entry></feed>