<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Bits and Pieces</title><link href="https://pshchelo.github.io/" rel="alternate"></link><link href="https://pshchelo.github.io/feeds/all.atom.xml" rel="self"></link><id>https://pshchelo.github.io/</id><updated>2024-12-18T00:00:00+02:00</updated><entry><title>Authentication flow with Keystone and OpenID Connect - with cURL</title><link href="https://pshchelo.github.io/keystone-oidc-curl.html" rel="alternate"></link><published>2024-06-12T00:00:00+03:00</published><updated>2024-12-18T00:00:00+02:00</updated><author><name>pas-ha</name></author><id>tag:pshchelo.github.io,2024-06-12:/keystone-oidc-curl.html</id><summary type="html">&lt;p class="first last"&gt;auth to keystone federated via oidc with cURL calls&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Recently I was tasked to provide an example curl commands to realize the
authentication process required to get an OpenStack Identity (Keystone) token
for a user federated via OpenID Connect.
Usually we tend to use already established libraries like
&lt;tt class="docutils literal"&gt;keystoneauth&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;gophercloud&lt;/tt&gt; for that, but since I not so long ago
had to dive into OpenID Connect anyway, this posed  an interesting task.&lt;/p&gt;
&lt;p&gt;I use
&lt;a class="reference external" href="https://www.mirantis.com/software/mirantis-openstack-for-kubernetes/"&gt;Mirantis OpenStack for Kubernetes&lt;/a&gt;
(naturally :-) ), that integrates
Keystone with Keycloak via OpenID Connect, OpenStack release is Caracal.&lt;/p&gt;
&lt;div class="section" id="pre-requisites"&gt;
&lt;h2&gt;Pre-requisites&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;OpenStack deployed and configured to use federation via OpenID Connect.&lt;/li&gt;
&lt;li&gt;Account in the Identity Provider allowing you to login to OpenStack.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;curl&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;jq&lt;/tt&gt; installed and available.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="required-access-info"&gt;
&lt;h3&gt;Required access info&lt;/h3&gt;
&lt;p&gt;Obtain required access info.
The commands below expect the following environment variables to be available.&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;OS_AUTH_URL&lt;/dt&gt;
&lt;dd&gt;the OpenStack Identity endpoint address. Note that examples below expect
a versioned endpoint (ends with &lt;tt class="docutils literal"&gt;/v3&lt;/tt&gt;), adapt URLs accordingly if yours
is not versioned.&lt;/dd&gt;
&lt;dt&gt;OS_DISCOVERY_ENDPOINT&lt;/dt&gt;
&lt;dd&gt;the URL of OpenID configuration discovery of the Identity Provider configured
in Keystone, usually ends with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;.../.well-known/openid-configuration&lt;/span&gt;&lt;/tt&gt;.&lt;/dd&gt;
&lt;dt&gt;OS_IDENTITY_PROVIDER&lt;/dt&gt;
&lt;dd&gt;Name of the identity provider configured in Keystone.&lt;/dd&gt;
&lt;dt&gt;OS_PROTOCOL&lt;/dt&gt;
&lt;dd&gt;Name of the protocol configured for this Identity Provider in Keystone.&lt;/dd&gt;
&lt;dt&gt;OS_CLIENT_ID&lt;/dt&gt;
&lt;dd&gt;The client name to use when contacting Identity Provider.
Note - this is NOT your OpenStack login.&lt;/dd&gt;
&lt;dt&gt;OS_CLIENT_SECRET&lt;/dt&gt;
&lt;dd&gt;The password to use for the Identity Provider client.
Usually it must be kept secret,
as in standard Web SSO application of OpenID Connect,
but here, as with Kubernetes + OpenID Connect,
the CLI client is required to know it.
Some Identity Providers allow to create clients without secrets (for example
Keycloak does allow that).&lt;/dd&gt;
&lt;dt&gt;OS_OPENID_SCOPE&lt;/dt&gt;
&lt;dd&gt;The scope to use for authentication, governs how much info about you the
Identity Provider will pass to the Service Provider - in this case you :-)
At least (and usually only) must be &lt;tt class="docutils literal"&gt;openid&lt;/tt&gt;.&lt;/dd&gt;
&lt;dt&gt;OS_USERNAME&lt;/dt&gt;
&lt;dd&gt;Your username with Identity Provider you want to login to OpenStack with.&lt;/dd&gt;
&lt;dt&gt;OS_PASSWORD&lt;/dt&gt;
&lt;dd&gt;Your password for your account with the Identity Provider.&lt;/dd&gt;
&lt;dt&gt;OS_PROJECT_NAME&lt;/dt&gt;
&lt;dd&gt;The name of the OpenStack project you want to authenticate to.&lt;/dd&gt;
&lt;dt&gt;OS_PROJECT_DOMAIN_ID&lt;/dt&gt;
&lt;dd&gt;ID of the OpenStack Identity domain that project belongs to.&lt;/dd&gt;
&lt;/dl&gt;
&lt;div class="section" id="get-it-from-horizon"&gt;
&lt;h4&gt;Get it from Horizon&lt;/h4&gt;
&lt;p&gt;The easiest way to obtain those is to login via federation to Horizon,
and download the RC file from the UI:&lt;/p&gt;
&lt;img alt="" src="https://pshchelo.github.io/images/keystone-oidc-curl/horizon-download-rc-file.png" /&gt;
&lt;p&gt;source this file into the active shell, it will ask you for your password&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;project&amp;gt;-openrc.sh
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In what follows, I am assuming you've done that already, so all the necessary
environment variables are present.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="authentication-flow"&gt;
&lt;h2&gt;Authentication Flow&lt;/h2&gt;
&lt;p&gt;Now let's start with curl-ing. I use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-sk&lt;/span&gt;&lt;/tt&gt; to silence the progress indicator
and, since I use development toy environment and self-signed TLS certificates,
ignore TLS verification failures.&lt;/p&gt;
&lt;p&gt;I am following the calls that would've been made when using &lt;tt class="docutils literal"&gt;keystoneauth&lt;/tt&gt;
library with &lt;tt class="docutils literal"&gt;v3oidcpassword&lt;/tt&gt; authentication type.&lt;/p&gt;
&lt;div class="section" id="get-oidc-token-endpoint"&gt;
&lt;h3&gt;Get OIDC token endpoint&lt;/h3&gt;
&lt;p&gt;Using the discovery endpoint, find the URL of token endpoint:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;token_endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sk&lt;span class="w"&gt; &lt;/span&gt;-X&lt;span class="w"&gt; &lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OS_DISCOVERY_ENDPOINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;.token_endpoint&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="get-the-oidc-access-token"&gt;
&lt;h3&gt;Get the OIDC access token&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-X&lt;span class="w"&gt; &lt;/span&gt;POST&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$token_endpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OS_CLIENT_ID&lt;/span&gt;:&lt;span class="nv"&gt;$OS_CLIENT_SECRET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;username=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OS_USERNAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;password=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OS_PASSWORD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;scope=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OS_OPENID_SCOPE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;grant_type=password&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-H&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Content-Type: application/x-www-form-urlencoded&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;.access_token&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The trick is the Content-Type - as per the
&lt;a class="reference external" href="https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest"&gt;OpenID Connect RFC&lt;/a&gt;
this is how it must be done with Form Serialization, not JSON.
The client id and client secret are used for HTTP Basic Authentication, again,
as per that RFC.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="get-the-unscoped-keystone-token"&gt;
&lt;h3&gt;Get the unscoped Keystone token&lt;/h3&gt;
&lt;p&gt;Now the OpenStack part. In OpenStack, tokens are issued and valid in various
&amp;quot;scopes&amp;quot; - project, domain, system or unscoped.&lt;/p&gt;
&lt;p&gt;With federation, API user is expected to first exchange the Identity Provider
token for unscoped OpenStack Identity token:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;unscoped_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sik&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-I&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-X&lt;span class="w"&gt; &lt;/span&gt;POST&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OS_AUTH_URL&lt;/span&gt;/OS-FEDERATION/identity_providers/&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OS_IDENTITY_PROVIDER&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/protocols/&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OS_PROTOCOL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/auth&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-H&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$access_token&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;x-subject-token&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;awk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tr&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;\r&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(the result of grep + awk has new line in the end, so need to trim that out
to put that value properly into JSON later).&lt;/p&gt;
&lt;p&gt;Here already we have some inconveniences with Bash:
the token arrives in the header, but the response body (in JSON)
also has some info.
We are ignoring it, but it may be quite useful in some applications.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="discover-authentication-scopes-optional"&gt;
&lt;h3&gt;Discover authentication scopes (optional)&lt;/h3&gt;
&lt;p&gt;If you do not have the intended scope of authentication at hand - project or
domain or system - you can now discover the available to you scopes by making
the following requests with unscoped token:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sik&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OS_AUTH_URL&lt;/span&gt;/auth/projects&lt;span class="w"&gt; &lt;/span&gt;-H&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;X-Auth-Token: &lt;/span&gt;&lt;span class="nv"&gt;$unscoped_token&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;.projects
curl&lt;span class="w"&gt; &lt;/span&gt;-sik&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OS_AUTH_URL&lt;/span&gt;/auth/domains&lt;span class="w"&gt; &lt;/span&gt;-H&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;X-Auth-Token: &lt;/span&gt;&lt;span class="nv"&gt;$unscoped_token&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;.domains
curl&lt;span class="w"&gt; &lt;/span&gt;-sik&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OS_AUTH_URL&lt;/span&gt;/auth/system&lt;span class="w"&gt; &lt;/span&gt;-H&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;X-Auth-Token: &lt;/span&gt;&lt;span class="nv"&gt;$unscoped_token&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;.system
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="prepare-json-for-scoped-token-request"&gt;
&lt;h3&gt;Prepare JSON for scoped token request&lt;/h3&gt;
&lt;p&gt;Generating JSON in Bash is very awkward due to double-quotes and lots of
escaping... just save the request JSON body to file (obviously not secure):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;token_request&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;mktemp&lt;span class="k"&gt;)&lt;/span&gt;
cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$token_request&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; EOJSON&lt;/span&gt;
&lt;span class="s"&gt;{&lt;/span&gt;
&lt;span class="s"&gt;  &amp;quot;auth&amp;quot;: {&lt;/span&gt;
&lt;span class="s"&gt;    &amp;quot;identity&amp;quot;: {&lt;/span&gt;
&lt;span class="s"&gt;      &amp;quot;methods&amp;quot;: [&lt;/span&gt;
&lt;span class="s"&gt;        &amp;quot;token&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;      ],&lt;/span&gt;
&lt;span class="s"&gt;      &amp;quot;token&amp;quot;: {&lt;/span&gt;
&lt;span class="s"&gt;        &amp;quot;id&amp;quot;: &amp;quot;$unscoped_token&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;      }&lt;/span&gt;
&lt;span class="s"&gt;    },&lt;/span&gt;
&lt;span class="s"&gt;    &amp;quot;scope&amp;quot;: {&lt;/span&gt;
&lt;span class="s"&gt;      &amp;quot;project&amp;quot;: {&lt;/span&gt;
&lt;span class="s"&gt;        &amp;quot;domain&amp;quot;: {&lt;/span&gt;
&lt;span class="s"&gt;          &amp;quot;id&amp;quot;: &amp;quot;$OS_PROJECT_DOMAIN_ID&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;        },&lt;/span&gt;
&lt;span class="s"&gt;        &amp;quot;name&amp;quot;: &amp;quot;$OS_PROJECT_NAME&amp;quot;&lt;/span&gt;
&lt;span class="s"&gt;      }&lt;/span&gt;
&lt;span class="s"&gt;    }&lt;/span&gt;
&lt;span class="s"&gt;  }&lt;/span&gt;
&lt;span class="s"&gt;}&lt;/span&gt;
&lt;span class="s"&gt;EOJSON&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="get-scoped-token"&gt;
&lt;h3&gt;Get scoped token&lt;/h3&gt;
&lt;p&gt;Biggest disadvantages here, as again, the token is in the headers,
but the response body contains a lot of useful info, including auth info
(UUIDs of project, domain etc, group assignments, roles if explicit),
and the Identity Catalog to discover the actual URL of the service endpoints
we want to acces with the received token.
Again, we skip all that useful info and only fetch the token:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nv"&gt;scoped_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sik&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-X&lt;span class="w"&gt; &lt;/span&gt;POST&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$OS_AUTH_URL&lt;/span&gt;/auth/tokens&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;@&lt;/span&gt;&lt;span class="nv"&gt;$token_request&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-H&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Content-Type: application/json&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;x-subject-token&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;awk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tr&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;\r&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Remove the temporary file with token request body (tiny security improvement):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;rm&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$token_request&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="use-scoped-token-to-make-request-to-an-openstack-service"&gt;
&lt;h2&gt;Use scoped token to make request to an OpenStack service&lt;/h2&gt;
&lt;p&gt;Here hardcoded endpoint is used, however base part of it could've
been discovered from the response body of the previous request.&lt;/p&gt;
&lt;p&gt;Get the list of available images:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-X&lt;span class="w"&gt; &lt;/span&gt;GET&lt;span class="w"&gt; &lt;/span&gt;https://glance.it.just.works/v2/images&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-H&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;X-Auth-Token: &lt;/span&gt;&lt;span class="nv"&gt;$scoped_token&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jq&lt;span class="w"&gt; &lt;/span&gt;.images
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I specifically use Glance in the example as it has no project UUID in the
endpoint, but many more services will need that, so their endpoints are better
to discover from the catalog that was in the body of the response when we got
ourselves the scoped token in the previous step.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-same-but-in-python"&gt;
&lt;h2&gt;The same but in Python&lt;/h2&gt;
&lt;p&gt;For comparison here are examples using Python:&lt;/p&gt;
&lt;div class="section" id="requests-only"&gt;
&lt;h3&gt;requests only&lt;/h3&gt;
&lt;p&gt;first, very manual example using &lt;tt class="docutils literal"&gt;requests&lt;/tt&gt; only, but now with proper
discovery of service endpoint:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;fed_auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_discovery_endpoint&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_DISCOVERY_ENDPOINT&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_identity_provider&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_IDENTITY_PROVIDER&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_protocol&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_PROTOCOL&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_openid_scope&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_OPENID_SCOPE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_client_secret&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_CLIENT_SECRET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_client_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_CLIENT_ID&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_username&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_USERNAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_password&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_PASSWORD&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_project_domain_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_PROJECT_DOMAIN_ID&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_project_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_PROJECT_NAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_auth_url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_AUTH_URL&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_region_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_REGION_NAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_interface&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_INTERFACE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_insecure&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;VERIFY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_cacert&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;VERIFY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_cacert&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_insecure&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;VERIFY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

&lt;span class="c1"&gt;# discover OIDC provider token endpoint&lt;/span&gt;
&lt;span class="n"&gt;discovery_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_discovery_endpoint&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;VERIFY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;token_endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;discovery_resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;token_endpoint&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# get OIDC access token&lt;/span&gt;
&lt;span class="n"&gt;access_req_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;username=&lt;/span&gt;&lt;span class="si"&gt;{os_username}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;password=&lt;/span&gt;&lt;span class="si"&gt;{os_password}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;scope=&lt;/span&gt;&lt;span class="si"&gt;{os_openid_scope}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;grant_type=password&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;access_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;token_endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;VERIFY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;application/x-www-form-urlencoded&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;access_req_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_client_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_client_secret&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;access_resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;access_token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Exchange OIDC access token for OpenStack Identity unscoped token&lt;/span&gt;
&lt;span class="n"&gt;unscoped_token_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{os_auth_url}&lt;/span&gt;&lt;span class="s2"&gt;/OS-FEDERATION/identity_providers/&lt;/span&gt;&lt;span class="si"&gt;{os_identity_provider}&lt;/span&gt;&lt;span class="s2"&gt;/protocols/&lt;/span&gt;&lt;span class="si"&gt;{os_protocol}&lt;/span&gt;&lt;span class="s2"&gt;/auth&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Authorization&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;VERIFY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;unscoped_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unscoped_token_resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x-subject-token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# (optional) use unscoped token to discover possible authorizaton scopes&lt;/span&gt;
&lt;span class="n"&gt;available_project_scopes_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{os_auth_url}&lt;/span&gt;&lt;span class="s2"&gt;/auth/projects&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;VERIFY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;X-Auth-Token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unscoped_token&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# exchange unscoped token and scope info for scoped token&lt;/span&gt;
&lt;span class="n"&gt;scoped_auth_req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;auth&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;identity&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;methods&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;token&amp;quot;&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unscoped_token&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;scope&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;&amp;quot;project&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;domain&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_project_domain_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="s2"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_project_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;scoped_token_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{os_auth_url}&lt;/span&gt;&lt;span class="s2"&gt;/auth/tokens&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;VERIFY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Content-Type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;application/json&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;scoped_auth_req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# more info on user, its roles and groups is in the JSON body of the response&lt;/span&gt;
&lt;span class="n"&gt;scoped_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scoped_token_resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x-subject-token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;catalog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scoped_token_resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;catalog&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;interface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_interface&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;public&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os_region_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;RegionOne&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# discover endpoint of the Image service&lt;/span&gt;
&lt;span class="n"&gt;image_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;catalog&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;image&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;image_service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Could not find image service in catalog&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;image_service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image_service&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;image_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;image_service&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;endpoints&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;interface&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;interface&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;region_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;image_api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Could not find required endpoint for image service&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;image_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image_api&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;image_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/v2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;image_api&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/v2&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# use scoped token to make request to image service endpoint&lt;/span&gt;
&lt;span class="c1"&gt;# list available images&lt;/span&gt;
&lt;span class="n"&gt;images_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;image_api&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/images&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;VERIFY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;X-Auth-Token&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scoped_token&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;images_resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="openstacksdk"&gt;
&lt;h3&gt;openstacksdk&lt;/h3&gt;
&lt;p&gt;and then, the example using &lt;tt class="docutils literal"&gt;openstacksdk&lt;/tt&gt; - a dedicated Python API library
for working with OpenStack clouds.
Note that with properly set up &lt;tt class="docutils literal"&gt;clouds.yaml&lt;/tt&gt;
&lt;a class="reference external" href="https://docs.openstack.org/openstacksdk/latest/user/config/configuration.html#config-files"&gt;configuration file&lt;/a&gt;
that could've been just 3 lines of code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;openstack&lt;/span&gt;

&lt;span class="n"&gt;fed_auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_auth_type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;v3oidcpassword&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_discovery_endpoint&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_DISCOVERY_ENDPOINT&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_identity_provider&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_IDENTITY_PROVIDER&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_protocol&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_PROTOCOL&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_openid_scope&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_OPENID_SCOPE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_client_secret&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_CLIENT_SECRET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_client_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_CLIENT_ID&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_username&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_USERNAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_password&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_PASSWORD&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_project_domain_id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_PROJECT_DOMAIN_ID&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_project_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_PROJECT_NAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_auth_url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_AUTH_URL&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_region_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_REGION_NAME&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_interface&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;OS_INTERFACE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;os_insecure&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;fed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openstack&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;load_yaml_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;fed_auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# if you save the auth and access info to a clouds.yaml file,&lt;/span&gt;
&lt;span class="c1"&gt;# https://docs.openstack.org/openstacksdk/latest/user/config/configuration.html#config-files&lt;/span&gt;
&lt;span class="c1"&gt;# then you don&amp;#39;t need env variables, and all the code above can be replaced&lt;/span&gt;
&lt;span class="c1"&gt;# with single call&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# fed = openstack.connect(cloud=&amp;lt;cloud name&amp;gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_images&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="work"></category><category term="openstack"></category><category term="keystone"></category><category term="federation"></category><category term="openid"></category><category term="oidc"></category><category term="curl"></category></entry><entry><title>Solving 2x2 Rubik's cube</title><link href="https://pshchelo.github.io/rubik2-solve.html" rel="alternate"></link><published>2020-12-13T00:00:00+02:00</published><updated>2020-12-13T00:00:00+02:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2020-12-13:/rubik2-solve.html</id><summary type="html">&lt;p class="first last"&gt;Algorithm for solving 2x2 Rubik's Cube.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="base-combinations"&gt;
&lt;h2&gt;Base combinations&lt;/h2&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Swap FRU &amp;lt;-&amp;gt; FRD  - &lt;tt class="docutils literal"&gt;R U R' U'&lt;/tt&gt; (with rotation)&lt;/p&gt;
&lt;p&gt;Repeat, and cubes are back to previous places but rotated.
Repeat 6 times changes nothing, everything comes back to original places&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Swap FRU &amp;lt;-&amp;gt; FLU - &lt;tt class="docutils literal"&gt;U R U' L' U R' U' L U&lt;/tt&gt; (with some rotation)&lt;/p&gt;
&lt;p&gt;The D layer stays unchanged, the BRU and BLU rotate too.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="base-algorithm"&gt;
&lt;h2&gt;Base algorithm&lt;/h2&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Build one layer, it becomes D-layer for now,
should be easy enough w/o any explicit algorithm.&lt;/li&gt;
&lt;li&gt;Use combo 2 to place the cubes of U layer in the correct places,
disregarding their rotation for now.&lt;/li&gt;
&lt;li&gt;Turn the cube upside down, the already built D-layer becomes U-layer.&lt;/li&gt;
&lt;li&gt;Start rotating the now D-layer using combo 1 x2 or x4 - do not worry that
U-layer becomes undone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;w/o rotating the whole cube&lt;/strong&gt;, use &lt;cite&gt;D&lt;/cite&gt; or &lt;cite&gt;D'&lt;/cite&gt; to place the next
incorrectly rotated cube of D layer to position FRD.&lt;/li&gt;
&lt;li&gt;Repeat step 4 and 5.&lt;/li&gt;
&lt;li&gt;Apart from some U or D rotation it should come together eventually when
rotating the last incorrect D-layer cube.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=AJCW5xTjmTk"&gt;Source&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="play"></category><category term="puzzles"></category><category term="rubik"></category></entry><entry><title>Testing pykube for OpenID Connect support</title><link href="https://pshchelo.github.io/pykube-oidc-refresh.html" rel="alternate"></link><published>2020-05-11T00:00:00+03:00</published><updated>2024-12-18T00:00:00+02:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2020-05-11:/pykube-oidc-refresh.html</id><summary type="html">&lt;p class="first last"&gt;Setting up local dev env to test OpenID Connect support in PyKube&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="why"&gt;
&lt;h2&gt;Why&lt;/h2&gt;
&lt;p&gt;During review of my &lt;a class="reference external" href="https://github.com/hjacobs/pykube/pull/49"&gt;PR#49&lt;/a&gt;
to the &lt;tt class="docutils literal"&gt;pykube&lt;/tt&gt; library to add OpenIDConnect token
refresh capabilities I was asked to provide some instructions on how to
setup a Kubernetes cluster with OpenIDConnect for
development/testing/verification of that PR.&lt;/p&gt;
&lt;p&gt;At work, we have a central common Keycloak instance running, which I was
testing my patch against.
I myself never set up something like this, so this seemed like a good
opportunity to get creative and learn something new in the process :-)&lt;/p&gt;
&lt;div class="section" id="update"&gt;
&lt;h3&gt;UPDATE&lt;/h3&gt;
&lt;p&gt;The PR in question was merged and is available in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pykybe-ng&lt;/span&gt; &amp;gt;= 20.10.0&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Also note the new home for &lt;a class="reference external" href="https://codeberg.org/hjacobs/pykube-ng"&gt;pykube-ng&lt;/a&gt;
at Codeberg.org.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="choosing-an-openid-provider"&gt;
&lt;h2&gt;Choosing an OpenID provider&lt;/h2&gt;
&lt;p&gt;Most available sources and tools I could find are using either
Google or Azure as OpenID providers together with Kubernetes.&lt;/p&gt;
&lt;p&gt;I don't have Azure account, and when I tried Google it seems that all
previous instructions were a bit outdated. Google seems to have
tightened its security around OpenID (which is understandable as &amp;quot;Login with
Google&amp;quot; is quite popular and may be abused). It now requires proper registered
domain for callbacks with properly signed TLS cert (not a self-signed one).
Both of those are overly restrictive / impossible for a local dev env.&lt;/p&gt;
&lt;p&gt;While searching for alternatives and more info on OpenID in general I stumbled
on very nice set of articles by Okta
(&lt;a class="reference external" href="https://developer.okta.com/blog/2017/07/25/oidc-primer-part-1"&gt;first part&lt;/a&gt;),
that helped me a lot with understanding the concept and flow.
So I decided to give Okta a try and set up my dev env there.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="setting-up-okta-as-idp"&gt;
&lt;h2&gt;Setting up Okta as IdP&lt;/h2&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Create free account on &lt;a class="reference external" href="https://developer.okta.com"&gt;https://developer.okta.com&lt;/a&gt;
Free tier is IMO quite enough for personal development (currently it caps
at 1k monthly active users and 5 apps/clients).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;On the Okta site Dashboard note the &amp;quot;Site URL&amp;quot; (see screenshot below).
This is going to be the base of the URL of the OpenID provider and will be
used quite a lot throughout this guide.&lt;/p&gt;
&lt;img alt="Okta dashboard" class="align-center" src="https://pshchelo.github.io/images/pykube-oidc-refresh/okta-dashboard.png" style="width: 100%;" /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Add a new application of &amp;quot;Web&amp;quot; type. OpenIDConnect supports several auth
flows and AFAIU only this one supports refresh tokens that kubernetes
clients rely on.&lt;/p&gt;
&lt;img alt="Okta dashboard" class="align-center" src="https://pshchelo.github.io/images/pykube-oidc-refresh/okta-new-web-app.png" style="width: 100%;" /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Configure new app (I called mine &lt;tt class="docutils literal"&gt;kube&lt;/tt&gt;)&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Default &lt;strong&gt;Login redirect URI&lt;/strong&gt; pointing to &lt;em&gt;localhost&lt;/em&gt; is almost good
enough, you just have to &lt;strong&gt;make sure it uses HTTPS scheme&lt;/strong&gt; -
Python's &lt;tt class="docutils literal"&gt;oauth2&lt;/tt&gt; lib is picky about that.
Change all other URIs to use &lt;tt class="docutils literal"&gt;https&lt;/tt&gt; as well for consistency.&lt;/li&gt;
&lt;li&gt;Make sure you enable &lt;strong&gt;Refresh Token&lt;/strong&gt; in &lt;strong&gt;Grant type allowed&lt;/strong&gt; -
after all we are about to test them in the first place.&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt="Okta dashboard" class="align-center" src="https://pshchelo.github.io/images/pykube-oidc-refresh/okta-app-configure.png" style="width: 100%;" /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;On the &amp;quot;General&amp;quot; settings of your new app there are now 3 details you going
to need further:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Login redirect URI&lt;/li&gt;
&lt;li&gt;Client ID&lt;/li&gt;
&lt;li&gt;Client secret (required by Okta, but can be optional in e.g. Keycloak)&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt="Okta dashboard" class="align-center" src="https://pshchelo.github.io/images/pykube-oidc-refresh/okta-app-overview.png" style="width: 100%;" /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Go to &amp;quot;Assignments&amp;quot; tab of application settings and check that there is at
least you yourself assigned as user allowed to use this app
(should be by default but better to double check).
As you see on screenshot I've added couple more dummy users to my org
to play with.&lt;/p&gt;
&lt;img alt="Okta dashboard" class="align-center" src="https://pshchelo.github.io/images/pykube-oidc-refresh/okta-app-assignments.png" style="width: 100%;" /&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="installing-and-configuring-microk8s"&gt;
&lt;h2&gt;Installing and configuring microk8s&lt;/h2&gt;
&lt;p&gt;I am using &lt;tt class="docutils literal"&gt;microk8s&lt;/tt&gt; installed on my local Ubuntu machine for testing.&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
sudo&lt;span class="w"&gt; &lt;/span&gt;snap&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;microk8s
&lt;/pre&gt;
&lt;p&gt;I use it with all plugins disabled, including RBAC one to simplify things.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://microk8s.io/docs/configuring-services"&gt;Edit&lt;/a&gt; arguments of
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;kube-apiserver&lt;/span&gt;&lt;/tt&gt; to add the following flags:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
--oidc-username-claim=email
--oidc-issuer-url=&amp;lt;okta-site-url&amp;gt;/oauth2/default
--oidc-client-id=&amp;lt;client-id&amp;gt;
&lt;/pre&gt;
&lt;p&gt;where &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;okta-site-url&amp;gt;&lt;/span&gt;&lt;/tt&gt; is is your site org URL (the one looking like
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;https://dev-NNNN.okta.com&lt;/span&gt;&lt;/tt&gt;) and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;client-id&lt;/span&gt;&lt;/tt&gt; is the &lt;em&gt;Client ID&lt;/em&gt; of the
client you've created in the previous steps.&lt;/p&gt;
&lt;p&gt;Restart &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;kube-apiserver&lt;/span&gt;&lt;/tt&gt; for changes to take effect.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="obtaining-proper-kube-config"&gt;
&lt;h2&gt;Obtaining proper kube config&lt;/h2&gt;
&lt;p&gt;Now is the tricky part where we need some actual code :-)&lt;/p&gt;
&lt;p&gt;Effectively we need to pretend we are that web application that we've set
with Okta to get the access, id and refresh tokens.
There are already OpenIDConnect helpers for that,
but most are either public cloud specific (Google, Azure),
or otherwise hardcoded to work with other OpenID providers like Keycloak.&lt;/p&gt;
&lt;p&gt;Again, to play with Python's libs implementing the OAuth2, I've written
my own small helper that theoretically should be compatible with most OpenID
providers with minimal changes.&lt;/p&gt;
&lt;script src="https://gist.github.com/pshchelo/952d247b4dec1bacc6e023a343e29ba8.js"&gt;&lt;/script&gt;&lt;p&gt;If you've been following this guide, you'd need to set some shell variables
first (here and forth I assume we are running in a virtualenv):&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pykube-ng&lt;span class="w"&gt; &lt;/span&gt;requests-oauthlib&lt;span class="w"&gt; &lt;/span&gt;oauthlib&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;# the same as oidc-issuer-url we've set up for kube-apiserver
&lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OAUTH_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your&lt;span class="w"&gt; &lt;/span&gt;okta&lt;span class="w"&gt; &lt;/span&gt;site&lt;span class="w"&gt; &lt;/span&gt;url&amp;gt;/oauth2/default&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;# copy from Login redirect URI on General tab of our Okta applicaiton settings
&lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OAUTH_REDIRECT_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://localhost:8080/authorization-code/callback&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;# copy Client ID from General tab of your Okta application settings
&lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OAUTH_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your&lt;span class="w"&gt; &lt;/span&gt;client&lt;span class="w"&gt; &lt;/span&gt;id&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;# copy Client secret from General tab of your Okta application settings
&lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OAUTH_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your&lt;span class="w"&gt; &lt;/span&gt;client&lt;span class="w"&gt; &lt;/span&gt;secret&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now run the script. It will open your default browser with the login to your
Okta site prompt. After logging in copy the &lt;strong&gt;whole URL&lt;/strong&gt; your browser
was redirected to and tried to open but failed (as there should be nothing
serving requests on &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;https://localhost:8080&lt;/span&gt;&lt;/tt&gt;)
and paste it back to the script prompt at the terminal&lt;/p&gt;
&lt;pre class="code shell literal-block"&gt;
$&lt;span class="w"&gt; &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;k8s-oidc-helper.py&lt;span class="w"&gt;
&lt;/span&gt;Please&lt;span class="w"&gt; &lt;/span&gt;go&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;REDUCTED&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;authorize&lt;span class="w"&gt; &lt;/span&gt;access.&lt;span class="w"&gt;
&lt;/span&gt;Enter&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;full&lt;span class="w"&gt; &lt;/span&gt;callback&lt;span class="w"&gt; &lt;/span&gt;URL&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;attempted&lt;span class="w"&gt; &lt;/span&gt;by&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;browser:&lt;span class="w"&gt; &lt;/span&gt;https://localhost:8080/authorization-code/callback?code&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;REDUCTED&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The script will output the token it got in return, as well as a snippet
to merge into the &lt;tt class="docutils literal"&gt;users&lt;/tt&gt; section of your kubeconfig (and persist some of
that info in the &lt;tt class="docutils literal"&gt;/tmp/kubeuser&lt;/tt&gt; file).&lt;/p&gt;
&lt;p&gt;Now edit your kubeconfig to add appropriate new context with the new user we've
just created and the microk8s cluster we've set up and we are ready to go:&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
kubectl&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;use-context&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;your&lt;span class="w"&gt; &lt;/span&gt;new&lt;span class="w"&gt; &lt;/span&gt;context&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;ns
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="testing-token-refresh-in-pukube-ng"&gt;
&lt;h2&gt;Testing token refresh in pukube-ng&lt;/h2&gt;
&lt;p&gt;Now how to verify &lt;tt class="docutils literal"&gt;pykube&lt;/tt&gt; functionality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;instantiate &lt;tt class="docutils literal"&gt;pykube&lt;/tt&gt; from this kubeconfig and context&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;access some kubernetes resources&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;for example something global like Namespaces&lt;/p&gt;
&lt;pre class="code shell literal-block"&gt;
$&lt;span class="w"&gt; &lt;/span&gt;python3&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;pykube&lt;span class="w"&gt;
&lt;/span&gt;&amp;gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;n.name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Namespace.objects&lt;span class="o"&gt;(&lt;/span&gt;api&lt;span class="o"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'default'&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'kube-node-lease'&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'kube-public'&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'kube-system'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;delete user's id-token from the context&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;alternatively you can wait for 1 hour (token expiry in Okta), but
do not access the cluster with kubectl and this context during wait time
as it will refresh the token for you&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;try to access k8s again&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;W/o &lt;a class="reference external" href="https://github.com/hjacobs/pykube/pull/49"&gt;PR#49&lt;/a&gt; the client can't
authorize.&lt;/p&gt;
&lt;p&gt;With it is successfully uses refresh-token together with the rest of the info
to get a new id token and persists it in the kubeconfig file.&lt;/p&gt;
&lt;p&gt;Whenever refresh token expires itself (not sure yet of the Okta defaults
for this) you will have to repeat &lt;a class="reference internal" href="#obtaining-proper-kube-config"&gt;Obtaining proper kube config&lt;/a&gt; part to
actually login (with password &lt;em&gt;etc&lt;/em&gt;) to get a new refresh token.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="links"&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;p&gt;Links I've found useful and interesting while digging in all this:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://developer.okta.com/blog/2017/07/25/oidc-primer-part-1"&gt;https://developer.okta.com/blog/2017/07/25/oidc-primer-part-1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/gini/dexter"&gt;https://github.com/gini/dexter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/micahhausler/k8s-oidc-helper"&gt;https://github.com/micahhausler/k8s-oidc-helper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/making/k8s-keycloak-oidc-helper"&gt;https://github.com/making/k8s-keycloak-oidc-helper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://blog.gini.net/frictionless-kubernetes-openid-connect-integration-f1c356140937"&gt;https://blog.gini.net/frictionless-kubernetes-openid-connect-integration-f1c356140937&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/okta/samples-python-flask/tree/master/okta-hosted-login"&gt;https://github.com/okta/samples-python-flask/tree/master/okta-hosted-login&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html#web-application-flow"&gt;https://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html#web-application-flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://medium.com/&amp;#64;mrbobbytables/kubernetes-day-2-operations-authn-authz-with-oidc-and-a-little-help-from-keycloak-de4ea1bdbbe"&gt;https://medium.com/&amp;#64;mrbobbytables/kubernetes-day-2-operations-authn-authz-with-oidc-and-a-little-help-from-keycloak-de4ea1bdbbe&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><category term="work"></category><category term="kubernetes"></category><category term="openid"></category><category term="oidc"></category><category term="okta"></category><category term="microk8s"></category><category term="pykube"></category></entry><entry><title>Performance testing of Ansible-deploy driver for OpenStack Ironic</title><link href="https://pshchelo.github.io/ansible-deploy-perf.html" rel="alternate"></link><published>2017-09-01T00:00:00+03:00</published><updated>2024-12-18T00:00:00+02:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2017-09-01:/ansible-deploy-perf.html</id><summary type="html">&lt;p class="first last"&gt;Comparative performance testing of ansible-deploy and agent ironic drivers&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="what-and-why-are-we-testing"&gt;
&lt;h2&gt;What and why are we testing&lt;/h2&gt;
&lt;p&gt;For more than a year me and my colleagues are developing and promoting a new
deploy driver interface for OpenStack ironic service.
Contrary to the standard &lt;tt class="docutils literal"&gt;direct&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;iscsi&lt;/tt&gt; deploy interfaces that depend
on &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ironic-python-agent&lt;/span&gt;&lt;/tt&gt; service (IPA) running in the deploy ramdisk,
this new &lt;tt class="docutils literal"&gt;ansible&lt;/tt&gt; deploy interface is using Ansible to provision the node,
with the steps to execute during provisioning or cleaning defined as Ansible
playbooks/roles/tasks &lt;em&gt;etc&lt;/em&gt;.
The main advantage of this approach is greater flexibility regarding
fine-tuning of the provisioning process this deploy interface allows.&lt;/p&gt;
&lt;p&gt;This &lt;tt class="docutils literal"&gt;ansible&lt;/tt&gt; deploy interface and classic ironic drivers using it are
already available as part of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ironic-staging-driver&lt;/span&gt;&lt;/tt&gt; project &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;,
and we are in the process of proposing &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt; to include this interface
to the ironic project itself.&lt;/p&gt;
&lt;p&gt;One of the topics raised during discussions is the performance
of this new driver interface.
Contrary to the &lt;tt class="docutils literal"&gt;direct&lt;/tt&gt; deploy interface that offloads most of the work
to be executed by IPA that runs on the node being provisioned,
this new interface involves executing code on the side of ironic-conductor
service, thus we were asked to show that the &lt;tt class="docutils literal"&gt;ansible&lt;/tt&gt; deploy interface
can cope with deploying several nodes in parallel (50 at least) without severe
performance degradation on the side of ironic-conductor service/host.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="testbed"&gt;
&lt;h2&gt;Testbed&lt;/h2&gt;
&lt;p&gt;Lab setup and tests were performed by my colleague Vasyl Saienko &lt;a class="footnote-reference" href="#footnote-3" id="footnote-reference-3"&gt;[3]&lt;/a&gt;,
with me mostly consulting on setting up and tuning the &lt;tt class="docutils literal"&gt;ansible&lt;/tt&gt; deploy
interface and Ansible itself.&lt;/p&gt;
&lt;p&gt;The lab consisted of 4 baremetal nodes with the following relevant stats:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;1x ironic host&lt;/dt&gt;
&lt;dd&gt;&lt;strong&gt;CPU&lt;/strong&gt;: Intel(R) Xeon(R) CPU E5-2620, 32 cores,
&lt;strong&gt;RAM&lt;/strong&gt;: 32GB&lt;/dd&gt;
&lt;dt&gt;3x enrolled into ironic&lt;/dt&gt;
&lt;dd&gt;&lt;strong&gt;CPU&lt;/strong&gt;: Intel(R) Xeon(R) CPU E5-2650, 32 cores
&lt;strong&gt;RAM&lt;/strong&gt;: 64GB&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;We have deployed a minimal single host ironic installation as of 7.0.2 version
(Ocata release) on the ironic host node using Mirantis Cloud Platform 1.1 &lt;a class="footnote-reference" href="#footnote-4" id="footnote-reference-4"&gt;[4]&lt;/a&gt;.
The Ansible-deploy interface was taken from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ironic-staging-drivers&lt;/span&gt;&lt;/tt&gt; project
as of stable/ocata branch.
The deployment also included OpenStack Identity (keystone)
and Networking (neutron) services, also of Ocata release.&lt;/p&gt;
&lt;p&gt;We used Ansible 2.3.2.0 &lt;tt class="docutils literal"&gt;pip&lt;/tt&gt;-installed from PyPI.&lt;/p&gt;
&lt;p&gt;We enrolled the 3 slave baremetal nodes in ironic,
deployed them with Ubuntu Xenial image,
created 100 VMs on each of them, and enrolled those to ironic using
&lt;tt class="docutils literal"&gt;virtualbmc&lt;/tt&gt; utility to simulate IPMI-capable hardware nodes.
This effectively gave us 300 ironic nodes to test deployment on.&lt;/p&gt;
&lt;p&gt;Overall order of the tests was the following:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;pxe_ipmitool_ansible&lt;/tt&gt; driver, deploy 50 nodes, repeat 3 times&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;agent_ipmitool&lt;/tt&gt; driver, deploy 50 nodes, repeat 3 times&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;agent_ipmitool&lt;/tt&gt; driver, deploy 100 nodes, repeat 3 times&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;pxe_ipmitool_ansible&lt;/tt&gt; driver, deploy 100 nodes, repeat 3 times&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The deploy ramdisk used for both drivers was the &lt;tt class="docutils literal"&gt;tinyipa&lt;/tt&gt; image
as of stable/ocata rebuilt for usage with &lt;tt class="docutils literal"&gt;ansible&lt;/tt&gt; deploy interface.
We provisioned nodes with standard Ubuntu Xenial cloud image.&lt;/p&gt;
&lt;p&gt;Concurrent deployment was done via trivial bash script calling ironic client
commands in a &lt;tt class="docutils literal"&gt;for&lt;/tt&gt; loop.
For each iteration &amp;quot;virtual&amp;quot; nodes to be provisioned were chosen
to be equally distributed between real baremetal nodes
to decrease possible congestion.&lt;/p&gt;
&lt;p&gt;Monitoring was performed with Cacti, with results presented and discussed below.&lt;/p&gt;
&lt;div class="section" id="lab-tuning"&gt;
&lt;h3&gt;Lab tuning&lt;/h3&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;The static image store was moved outside of ironic node&lt;ul&gt;
&lt;li&gt;due to physical network layout of the lab,
this separate storage had better connection speed with baremetal nodes,
and thus was performing better&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The timeout for downloading the image &lt;a class="footnote-reference" href="#footnote-5" id="footnote-reference-5"&gt;[5]&lt;/a&gt; was increased.&lt;ul&gt;
&lt;li&gt;Current hard-coded value is more suitable for small &lt;tt class="docutils literal"&gt;cirros&lt;/tt&gt; image
used in OpenStack CI,
and turned out to be not sufficient for downloading the image
we were using given performance of our image store&lt;/li&gt;
&lt;li&gt;We plan to make such values actually configurable later&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We switched Ansible to use &lt;tt class="docutils literal"&gt;paramiko&lt;/tt&gt; transport instead of the default
&lt;tt class="docutils literal"&gt;smart&lt;/tt&gt; &lt;a class="footnote-reference" href="#footnote-6" id="footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;We experienced weird problems with SSH timeouts both on initial connection
and when executing tasks.&lt;/li&gt;
&lt;li&gt;There are number of bugs reported against Ansible that might be relevant,
&lt;em&gt;e.g.&lt;/em&gt; &lt;a class="footnote-reference" href="#footnote-7" id="footnote-reference-7"&gt;[7]&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Using &lt;tt class="docutils literal"&gt;paramiko&lt;/tt&gt; for SSH may have introduced an extra performance cost
compared to the native SSH binaries.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We increased the &lt;tt class="docutils literal"&gt;internal_poll_interval&lt;/tt&gt; Ansible configuration setting
&lt;a class="footnote-reference" href="#footnote-8" id="footnote-reference-8"&gt;[8]&lt;/a&gt; to &lt;tt class="docutils literal"&gt;0.01&lt;/tt&gt; (from default &lt;tt class="docutils literal"&gt;0.001&lt;/tt&gt;).&lt;ul&gt;
&lt;li&gt;Available since Ansible 2.2, this setting was specifically introduced for
better CPU-wise performance of Ansible in use-cases that do not require
responsiveness of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ansible-playbook&lt;/span&gt;&lt;/tt&gt; process console output.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="results-and-discussion"&gt;
&lt;h2&gt;Results and discussion&lt;/h2&gt;
&lt;div class="section" id="nodes-per-driver"&gt;
&lt;h3&gt;Nodes per driver&lt;/h3&gt;
&lt;p&gt;This plot shows the number of nodes registered per each driver
to set time frame reference for further graphs.&lt;/p&gt;
&lt;img alt="Nodes per driver" class="align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/node-by-driver100.png" /&gt;
&lt;p&gt;And we immediately see one of the troubles we stumbled upon - the dips in the
second graph around 10:35 and 11:30.
These graphs were plotted by Cacti periodically polling the ironic API
for number of nodes - and at these points requests simply timed out.
It happened for both drivers, so we tend to attribute this to the fact that
ironic API was running as the &lt;strong&gt;eventlet sever&lt;/strong&gt; instead of WSGI behind a more
robust webserver (Note that running ironic as WSGI app was not yet officially
supported in Ocata release).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="nodes-by-state"&gt;
&lt;h3&gt;Nodes by state&lt;/h3&gt;
&lt;p&gt;This plot shows the number of nodes in either &amp;quot;deploying/wait-callback&amp;quot;
or &amp;quot;active&amp;quot; state.&lt;/p&gt;
&lt;img alt="Active vs being deployed nodes" class="align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/ironic-nodes100.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="ironic-host-performance-stats"&gt;
&lt;h3&gt;Ironic host performance stats&lt;/h3&gt;
&lt;div class="section" id="batches-of-50"&gt;
&lt;h4&gt;Batches of 50&lt;/h4&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;CPU usage&lt;/dt&gt;
&lt;dd&gt;&lt;img alt="CPU usage, batches of 50" class="first last align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/cpu-usage.png" /&gt;
&lt;/dd&gt;
&lt;dt&gt;Load average&lt;/dt&gt;
&lt;dd&gt;&lt;img alt="System load, batches of 50" class="first last align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/load-average.png" /&gt;
&lt;/dd&gt;
&lt;dt&gt;Memory usage&lt;/dt&gt;
&lt;dd&gt;&lt;img alt="Memory usage, batches of 50" class="first last align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/memory-usage.png" /&gt;
&lt;/dd&gt;
&lt;dt&gt;TCP counters&lt;/dt&gt;
&lt;dd&gt;&lt;img alt="TCP counters, batches of 50" class="first last align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/tcp-counters.png" /&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;The sharp spikes in CPU utilization are well attributed to the TFTP server
serving multiple concurrent requests.&lt;/p&gt;
&lt;p&gt;We also see that using &lt;tt class="docutils literal"&gt;ansible&lt;/tt&gt; deploy interface consumes more CPU
(about 3% at peaks) and more RAM (about 3 GB) than agent-deploy.
This is due to the task execution engine (Ansible) is being run locally on
conductor instead of remotely on the node being deployed (IPA).&lt;/p&gt;
&lt;p&gt;Nevertheless the overall time to provision all nodes and the average CPU load
is nearly the same,
and the toll multiple Ansible processes take on the conductor node
is well within of what a real server suitable for such scaled baremetal cloud
can handle.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="overall-test-both-50-and-100-batches"&gt;
&lt;h4&gt;Overall test (both 50 and 100 batches)&lt;/h4&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;CPU usage&lt;/dt&gt;
&lt;dd&gt;&lt;img alt="CPU usage, total (batches of 50 and 100)" class="first last align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/cpu-usage100.png" /&gt;
&lt;/dd&gt;
&lt;dt&gt;Load average&lt;/dt&gt;
&lt;dd&gt;&lt;img alt="System load, total (batches of 50 and 100)" class="first last align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/load-average100.png" /&gt;
&lt;/dd&gt;
&lt;dt&gt;Memory usage&lt;/dt&gt;
&lt;dd&gt;&lt;img alt="Memory usage, total (batches of 50 and 100)" class="first last align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/memory-usage100.png" /&gt;
&lt;/dd&gt;
&lt;dt&gt;TCP counters&lt;/dt&gt;
&lt;dd&gt;&lt;img alt="TCP counters, total (batches of 50 and 100)" class="first last align-center" src="https://pshchelo.github.io/images/ansible-deploy-performance/tcp-counters100.png" /&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;As expected, using &lt;tt class="docutils literal"&gt;direct&lt;/tt&gt; driver interface scales better whith increasing
the number of nodes and is close to &lt;tt class="docutils literal"&gt;O(1)&lt;/tt&gt;,
while overhead of using &lt;tt class="docutils literal"&gt;ansible&lt;/tt&gt; deploy interface scales closer to &lt;tt class="docutils literal"&gt;O(n)&lt;/tt&gt;
of number of nodes, especially for RAM consumption.&lt;/p&gt;
&lt;p&gt;We tend to attribute such scaling difference to the fact
that current internal architecture of ironic does not allow us to use Ansible
as it was designed,
with one &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ansible-playbook&lt;/span&gt;&lt;/tt&gt; process executing the same playbook with
identical input variables against several nodes.
Instead, we launch separate &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ansible-playbook&lt;/span&gt;&lt;/tt&gt; process for each node
even when nodes are being provisioning with the same image and other settings,
which obviously has negative impact on resources used.&lt;/p&gt;
&lt;p&gt;This difference has to be taken into account when planning an (under)cloud
ironic deployment that is going to allow usage the &lt;tt class="docutils literal"&gt;ansible&lt;/tt&gt; deploy interface.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Overall we think that the &lt;tt class="docutils literal"&gt;ansible&lt;/tt&gt; deploy interface performs and scales
within acceptable limits on a quite standard server hardware.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="references"&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="http://git.openstack.org/cgit/openstack/ironic-staging-drivers/tree/ironic_staging_drivers/ansible"&gt;http://git.openstack.org/cgit/openstack/ironic-staging-drivers/tree/ironic_staging_drivers/ansible&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://review.openstack.org/#/c/241946/"&gt;https://review.openstack.org/#/c/241946/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://launchpad.net/~vsaienko"&gt;https://launchpad.net/~vsaienko&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://www.mirantis.com/software/mcp/"&gt;https://www.mirantis.com/software/mcp/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-5" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="http://git.openstack.org/cgit/openstack/ironic-staging-drivers/tree/ironic_staging_drivers/ansible/playbooks/roles/deploy/tasks/download.yaml?h=stable/ocata#n10"&gt;http://git.openstack.org/cgit/openstack/ironic-staging-drivers/tree/ironic_staging_drivers/ansible/playbooks/roles/deploy/tasks/download.yaml?h=stable/ocata#n10&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-6" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="http://docs.ansible.com/ansible/latest/intro_configuration.html#transport"&gt;http://docs.ansible.com/ansible/latest/intro_configuration.html#transport&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-7" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-7"&gt;[7]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://github.com/ansible/ansible/issues/24035"&gt;https://github.com/ansible/ansible/issues/24035&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-8" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-8"&gt;[8]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="http://docs.ansible.com/ansible/latest/intro_configuration.html#internal-poll-interval"&gt;http://docs.ansible.com/ansible/latest/intro_configuration.html#internal-poll-interval&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="work"></category><category term="openstack"></category><category term="ironic"></category><category term="ansible"></category><category term="deploy"></category></entry><entry><title>Moving on with OpenStack</title><link href="https://pshchelo.github.io/oo-moving-on.html" rel="alternate"></link><published>2017-03-07T00:00:00+02:00</published><updated>2024-12-18T00:00:00+02:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2017-03-07:/oo-moving-on.html</id><summary type="html">&lt;p class="first last"&gt;stay in community while moving along&lt;/p&gt;
</summary><content type="html">&lt;p&gt;So, you are changing jobs and moving to a next big thing,
but would like to keep your community profile in OpenStack or
probably will continue working on OpenStack on your new place?&lt;/p&gt;
&lt;p&gt;Below are a couple of things that you may consider to do to keep your
profile in OpenStack community accessible, and some other tips.&lt;/p&gt;
&lt;div class="section" id="accounts"&gt;
&lt;h2&gt;Accounts&lt;/h2&gt;
&lt;p&gt;Basically the main account to change is Ubuntu One -
it ties to both Launchpad and Openstack.org&lt;/p&gt;
&lt;p&gt;After each change ensure you can login with new, personal credentials.&lt;/p&gt;
&lt;div class="section" id="ubuntuone"&gt;
&lt;h3&gt;UbuntuOne&lt;/h3&gt;
&lt;p&gt;Change you primary e-mail to non-corporate one.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="launchpad"&gt;
&lt;h3&gt;Launchpad&lt;/h3&gt;
&lt;p&gt;Change you primary e-mail to non-corporate one.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="openstack"&gt;
&lt;h3&gt;OpenStack&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://www.openstack.org/profile/"&gt;https://www.openstack.org/profile/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Change your primary e-mail to a non-corporate one.&lt;/p&gt;
&lt;p&gt;Change affiliation.
Note, that if you will be officially working on OpenStack on your new job,
your new company should have signed the corporate license agreement
with OpenStack foundation.
If you change your affiliation to 'Independent',
you'll have to sign a new license agreement for independent contributors.&lt;/p&gt;
&lt;p&gt;If concerned about Stackalytics metrics, AFAIK for some time already it uses
affiliation from o.o (and not the e-mail domain)
to track contributions per-company.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="gerrit"&gt;
&lt;h3&gt;Gerrit&lt;/h3&gt;
&lt;div class="section" id="upstream"&gt;
&lt;h4&gt;Upstream&lt;/h4&gt;
&lt;p&gt;&lt;a class="reference external" href="https://review.openstack.org"&gt;https://review.openstack.org&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Add your non-corporate e-mail, make it a primary one.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="other-downstream-gerrits"&gt;
&lt;h4&gt;Other downstream Gerrits&lt;/h4&gt;
&lt;p&gt;Depending on whether there are open repos at all, might consider trying to add
your personal e-mail to the registered e-mail addresses.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="github"&gt;
&lt;h3&gt;GitHub&lt;/h3&gt;
&lt;p&gt;Ensure your primary e-mail is a non-corporate one.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="git"&gt;
&lt;h3&gt;Git&lt;/h3&gt;
&lt;p&gt;Don't forget to change your user.email in the &lt;tt class="docutils literal"&gt;.gitconfig&lt;/tt&gt; accordingly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="irc-irccloud"&gt;
&lt;h3&gt;IRC/IRCCloud&lt;/h3&gt;
&lt;p&gt;If you were using that, change your e-mail to non-corporate one.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="code"&gt;
&lt;h3&gt;Code&lt;/h3&gt;
&lt;p&gt;If you have any running envs (e.g.DevStack) with upstream code -
push it to OpenStack's gerrit or to the forks on your personal
GitHub/BitBucket etc.&lt;/p&gt;
&lt;p&gt;If you have any running envs with downstream code -
push it to appropriate downstream repo/Gerrit.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="others"&gt;
&lt;h2&gt;Others&lt;/h2&gt;
&lt;div class="section" id="mail"&gt;
&lt;h3&gt;Mail&lt;/h3&gt;
&lt;p&gt;Consider exporting mail filters, usually people end up with quite elaborate
setups to sort the usual mail flood.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="docs"&gt;
&lt;h3&gt;Docs&lt;/h3&gt;
&lt;p&gt;GDrive/Confluence/Dropbox etc - whatever corporate tool you were using.&lt;/p&gt;
&lt;p&gt;Export personal notes etc. &lt;em&gt;Beware of sensitive nature of some documents&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="work"></category><category term="openstack"></category><category term="community"></category></entry><entry><title>Graphical VNC console in ironic - early prototype</title><link href="https://pshchelo.github.io/vnc-in-ironic.html" rel="alternate"></link><published>2016-12-12T00:00:00+02:00</published><updated>2016-12-12T00:00:00+02:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2016-12-12:/vnc-in-ironic.html</id><summary type="html">&lt;p class="first last"&gt;enabling VNC access to nova instances deployed to ironic baremetal nodes&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In integrated case (working as part of OpenStack deployment) nova instances
deployed on ironic baremetal nodes have certain limitations compared to
standard virtual machines created by nova.
In particular it is not currently possible to access the graphical console
of such instance for example via horizon Dashboard.&lt;/p&gt;
&lt;p&gt;This is about to change with ironic community starting to work &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;
on introducing a framework for graphical console access for baremetal nodes.
As each hardware vendor implements a different way to provide graphical
console access, the framework is planned to be quite generic,
leaving details of actual graphical console configuration and management
to a proposed GraphicalConsole interface of an ironic driver.&lt;/p&gt;
&lt;p&gt;One interesting hardware to consider in this regard is Dell servers supporting
iDRACv7 or newer &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.
The iDRAC firmware on such servers supports native access to the server’s
graphical console over OpenVNC-compatible protocol directly,
without a need of proprietary VNC proxies or clients.
An administrator who has appropriate access to the iDRAC configuration can
enable this built-in VNC server and set the password, connection timeout and
SSL encryption options.&lt;/p&gt;
&lt;p&gt;In order to test the VNC capabilities of such hardware I have implemented
a prototype &lt;a class="footnote-reference" href="#footnote-3" id="footnote-reference-3"&gt;[3]&lt;/a&gt; of a graphical console interface for the DRAC driver.
It uses WS-MAN HTTP API (as the rest of DRAC-specific driver interfaces)
to toggle the VNC server feature on and off and set its properties.
I have also created a prototype &lt;a class="footnote-reference" href="#footnote-4" id="footnote-reference-4"&gt;[4]&lt;/a&gt; of &lt;tt class="docutils literal"&gt;get_vnc_console&lt;/tt&gt; method for ironic
virt-driver in nova.
As a result, I was able to get access to the graphical console in
horizon Dashboard for the nova instance deployed on top of Dell R630 server
managed by ironic.&lt;/p&gt;
&lt;img alt="Screenshot of a VNC console of the baremetal node in horizon Dashboard" class="align-center" src="https://pshchelo.github.io/images/ironic-vnc-console-files/bm-vnc-console-in-horizon.png" style="width: 100%;" /&gt;
&lt;div class="section" id="lessons-learned"&gt;
&lt;h2&gt;Lessons learned&lt;/h2&gt;
&lt;p&gt;Of course no prototype is complete and without any bugs/problems discovered
during testing.
Here is what I’ve been hitting my head and hacking around while making
this to work:&lt;/p&gt;
&lt;div class="section" id="prototype-limitations"&gt;
&lt;h3&gt;Prototype limitations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;This prototype is done prior to the generic graphical console framework
implementation done in ironic.
Thus the prototype implementation is for now overriding the existing serial
console interface in a specifically created for this purpose ironic driver.
That means currently it is not possible to have both serial console
and graphical console.&lt;/p&gt;
&lt;p&gt;Conveniently though, the proposed base GraphicalConsole interface will have
the same API as the current Console (SerialConsole in the future) interface.
This means that once the generic framework for graphical console interfaces
is implemented in ironic, this prototype can be plugged as graphical
console interface basically as-is.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The interface implementation is using low-level WS-MAN Python client calls
for now since support for managing iDracCardService is yet lacking
from python-dracclient.
The work to enable this functionality is already ongoing in the community
though.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The ironic virt-driver changes are rather specific for this particular case
to let me quickly test this functionality.
It will be changed after the generic graphical console is implemented in
Ironic and required complementary functionality is available in
python-ironicclient.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="idrac-vnc-limitations"&gt;
&lt;h3&gt;iDrac VNC limitations&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;OpenVNC implementation in iDRAC does not seem to be complete as noVNC can
not properly connect to it resulting in an apparently connected console
with no graphical output &lt;a class="footnote-reference" href="#footnote-5" id="footnote-reference-5"&gt;[5]&lt;/a&gt;.
A single passed encoding parameter must be disabled in noVNC code.
I had to resort to noVNC patched accordingly, but the implications of such
patch on access to standard VM graphical console are yet to be tested.&lt;/li&gt;
&lt;li&gt;Password must be set on the VNC server as noVNC can not connect to it
otherwise.
It seems setting the password for the iDRAC VNC server to None/empty string
still results in VNC server requesting a password on connection,
but noVNC can not accept an empty password in its password prompt.
I am not sure if this should be considered a bug in iDRAC VNC server or
in noVNC.&lt;/li&gt;
&lt;li&gt;I have not tested yet how iDRAC VNC server works with noVNC when SSL is
enabled in iDRAC VNC Server.&lt;/li&gt;
&lt;li&gt;The iDRAC VNC server is limited to a single VNC session at a time,
so it is not really multi-user setup.
On the other hand this still might suffice for undercloud-like use cases
such as TrippleO.&lt;/li&gt;
&lt;li&gt;Note that in the current prototype, all nodes running nova-novncproxy
service (or the single one specified as &lt;tt class="docutils literal"&gt;vncserver_proxyclient_address&lt;/tt&gt;
in config for nova-compute with ironic virt-driver) must effectively have
access to the BMC network as the built-in iDRAC VNC server is serving from
its own BMC IP address.
Care has to be taken to setup such proxying securely in a clustered nova
deployment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nevertheless, this seems like an interesting and promising development on
the hardware market.
I consider it as yet another small step on the way forward to close the gap
between baremetal and virtual servers in OpenStack and enable a unified user
experience for compute service.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="references"&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://review.openstack.org/#/c/306074/"&gt;https://review.openstack.org/#/c/306074/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="http://en.community.dell.com/cfs-file/__key/telligent-evolution-components-attachments/13-4491-00-00-20-44-10-34/AccessingRemoteDesktop_5F00_using_5F00_VNC_5F00_on_5F00_iDRAC.pdf"&gt;http://en.community.dell.com/cfs-file/__key/telligent-evolution-components-attachments/13-4491-00-00-20-44-10-34/AccessingRemoteDesktop_5F00_using_5F00_VNC_5F00_on_5F00_iDRAC.pdf&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://review.openstack.org/#/c/396661/"&gt;https://review.openstack.org/#/c/396661/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://review.openstack.org/#/c/398270/"&gt;https://review.openstack.org/#/c/398270/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-5" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a class="reference external" href="https://github.com/kanaka/noVNC/issues/712"&gt;https://github.com/kanaka/noVNC/issues/712&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="work"></category><category term="ironic"></category><category term="vnc"></category><category term="drac"></category></entry><entry><title>Азы Git и Gerrit workflow в проектах OpenStack</title><link href="https://pshchelo.github.io/git-gerrit-basics-ru.html" rel="alternate"></link><published>2016-09-20T00:00:00+03:00</published><updated>2024-12-18T00:00:00+02:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2016-09-20:/git-gerrit-basics-ru.html</id><summary type="html">&lt;p class="first last"&gt;Quick-n-Dirty opinionated introduction for beginners, in Russian.&lt;/p&gt;
</summary><content type="html">&lt;!--  --&gt;
&lt;blockquote&gt;
&amp;quot;git gets easier once you get the basic idea
that branches are homeomorphic endofunctors
mapping submanifolds of a Hilbert space&amp;quot;
&lt;a class="reference external" href="https://twitter.com/tabqwerty/status/45611899953491968"&gt;chi wai lau (&amp;#64;tabqwerty)&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Осторожно&lt;/strong&gt; - много англицизмов и моего личного мнения.&lt;/p&gt;
&lt;div class="contents topic" id="topic-1"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;О чем тут вообще&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#section-1" id="toc-entry-1"&gt;Самое Главное&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#git" id="toc-entry-2"&gt;Git&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#section-2" id="toc-entry-3"&gt;Основные конструкции&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#section-3" id="toc-entry-4"&gt;Индекс&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#section-4" id="toc-entry-5"&gt;Бранчи и Теги&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#remotes" id="toc-entry-6"&gt;Remotes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#very-basic-git-workflow" id="toc-entry-7"&gt;Very Basic git workflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#merg" id="toc-entry-8"&gt;Mergе и конфликты&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#cherry-pick" id="toc-entry-9"&gt;Cherry-pick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#rebase" id="toc-entry-10"&gt;Rebase&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#section-5" id="toc-entry-11"&gt;Правки и изменения истории&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#tools" id="toc-entry-12"&gt;Tools&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#section-6" id="toc-entry-13"&gt;Для любителей кнопочек и менюшечек&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#section-7" id="toc-entry-14"&gt;Для ковбоев консоли&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#fun" id="toc-entry-15"&gt;Fun&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#gerrit-workflow-openstack" id="toc-entry-16"&gt;Gerrit Workflow в OpenStack&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#openstack-english" id="toc-entry-17"&gt;Специфика сообщества OpenStack (english)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#git-review" id="toc-entry-18"&gt;git-review&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#basic-workflow" id="toc-entry-19"&gt;Basic workflow&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#change-id" id="toc-entry-20"&gt;Change-Id&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#section-8" id="toc-entry-21"&gt;Работа над новым, независимым изменением (баг, фича)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#section-9" id="toc-entry-22"&gt;Правим свой старый патч&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#rebase-or-not-rebase" id="toc-entry-23"&gt;Rebase or not Rebase?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="section-1"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-1"&gt;Самое Главное&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Не лениться и вдумчиво читать то, что&lt;/strong&gt; &lt;tt class="docutils literal"&gt;git&lt;/tt&gt; &lt;strong&gt;или&lt;/strong&gt; &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git-review&lt;/span&gt;&lt;/tt&gt;
&lt;strong&gt;выводит в командную строку.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Там очень часто весьма внятно описано что пошло или может пойти не так,
и какие есть варианты действий, с примерами команд&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="git"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-2"&gt;Git&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Чумовая книжечка -
&lt;a class="reference external" href="http://jwiegley.github.io/git-from-the-bottom-up/"&gt;Git from the Bottom Up&lt;/a&gt;
by John Wiegley (CC BY-ND 4.0).
Картинки скопированы оттуда, и общая канва повествования немного тоже.&lt;/p&gt;
&lt;div class="section" id="section-2"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-3"&gt;Основные конструкции&lt;/a&gt;&lt;/h3&gt;
&lt;img alt="кирпичики Гита" src="https://pshchelo.github.io/images/git-gerrit-basics/commits.png" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;Blob&lt;/strong&gt; - объект хранящий “файл”.
То, ради чего все затевалось.
Определяется своим хэшем, который определяется только размером файла
и его содержанием.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tree&lt;/strong&gt; - содержит ссылки на блобы и другие tree а также метадату для них.
Тоже имеет хэш, определяемый хэшами содержимого и метадатой.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commit&lt;/strong&gt; - содержит одно дерево, и ссылки на родительские коммиты,
формируя историю.
Тоже имеет хэш, определяемый всем этим - содержанием дерева,
коммит мессаджем и хэшами родительских коммитов.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Все остальное опирается на эти три базовых идеи.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="section-3"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-4"&gt;Индекс&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;В отличие от (большинства?) других систем контроля версий в Git реализована
двухступенчатая процедура внесения изменений - через &lt;em&gt;Index&lt;/em&gt;
(он же &lt;em&gt;Staging Area&lt;/em&gt;).&lt;/p&gt;
&lt;img alt="основы основ" src="https://pshchelo.github.io/images/git-gerrit-basics/lifecycle.png" /&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;Working area&lt;/strong&gt; - реальные файлы на файловой системе&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Index&lt;/strong&gt; - &amp;nbsp;изменения которые будут занесены в репозиторий как следующий
коммит&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repository&lt;/strong&gt; - хранилище коммитов&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="section-4"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-5"&gt;Бранчи и Теги&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Бранчи и теги - не более чем символические ссылки на commit id
имеющие человеко-читаемые имена.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;HEAD&lt;/tt&gt; - специальная ссылка на текущее (последнее чекаутное)
состояние Working Tree.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;&amp;lt;ref&amp;gt;^&lt;/tt&gt; - родитель коммита &amp;lt;ref&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;ref&amp;gt;^^&lt;/span&gt;&lt;/tt&gt; - второй родитель (есть однозначный типа MRO,
но точно не помню какой).
В принцие может быть больше двух родителей (^^^...)&amp;nbsp;aka octopus merge,
но в жизни пока не встречал/не приходилось делать.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;ref&amp;gt;~N&lt;/span&gt;&lt;/tt&gt; - Nый родитель вдаль по истории&lt;/p&gt;
&lt;p&gt;Основное отличие тегов в том что для них нельзя просто переопределить
ссылку на коммит - надо только удалить старый тег и создать такой же новый.
Но &lt;em&gt;IMnsHO&lt;/em&gt; тому кто двигает теги надо отрубать руки.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="remotes"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-6"&gt;Remotes&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ремоуты - это специальный вид бранчей, которые указывают на коммиты
на удаленном &amp;nbsp;репозитории (точнее в его локальной копии).&lt;/p&gt;
&lt;p&gt;Можно - и часто нужно! - иметь много remotes в своей локальной репе.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;remote&lt;span class="w"&gt; &lt;/span&gt;-v
git&lt;span class="w"&gt; &lt;/span&gt;remote&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;remote-name&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;url&amp;gt;
git&lt;span class="w"&gt; &lt;/span&gt;fetch&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;remote-name&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="very-basic-git-workflow"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-7"&gt;Very Basic git workflow&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# создаем новый репозиторий&lt;/span&gt;
mkdir&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;repo&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;init
&lt;span class="c1"&gt;# или стягиваем готовый&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;remote-url&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;path&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;
&lt;span class="c1"&gt;# hack on code&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--patch&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# заносим изменения в индекс&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;“My&lt;span class="w"&gt; &lt;/span&gt;commit”&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# сохраняем индекс как новый коммит&lt;/span&gt;
&lt;span class="c1"&gt;# git push [remote] # выкладываем изменения в удаленный репозиторий&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Интерактивный &lt;tt class="docutils literal"&gt;git add &lt;span class="pre"&gt;--patch&lt;/span&gt;&lt;/tt&gt; позволяет заносить в индекс отдельные
куски изменений (&lt;em&gt;hunks&lt;/em&gt;), а не весь файл целиком.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="merg"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-8"&gt;Mergе и конфликты&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Создание коммита с несколькими родителями.&lt;/p&gt;
&lt;p&gt;Часта ситуация когда некоторые файлы отличаются в обоих родителях,
и алгоритмы Git (на самом деле весьма продвинутые) не могут однозначно
определить, как должна выглядеть их “сумма”.
Появляются merge conflicts которые надо чинить.&lt;/p&gt;
&lt;p&gt;Ищем в файлах строчки&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&amp;lt;&amp;lt;&amp;lt;[some-ref]
# код того куда мержим
====
# код того что примерживаем
&amp;gt;&amp;gt;&amp;gt;[other-ref]
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;И выбираем какая версия больше нравится. А может и переписываем кусок совсем.&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;git mergetool&lt;/dt&gt;
&lt;dd&gt;удобная штука, которая из коробки умеет работать со многими
редакторами/сравнителями (vimdiff, meld, diffuse, WinMerge, kdiff3 и т.п.).
Настраивается через &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git-config&lt;/span&gt;&lt;/tt&gt;.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Обычно в редакторе будут файлы заканчивающиеся на:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;BASE&lt;/tt&gt; - версия файла из ближайшего общего предка&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;LOCAL&lt;/tt&gt; - то куда мержится&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;REMOTE&lt;/tt&gt; - то что мержится&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="cherry-pick"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-9"&gt;Cherry-pick&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Берет дифф данного коммита и пытается сделать коммит с таким же диффом
поверх другого (по сути делает копию коммита, но с другим родителем).
Возможны конфликты.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="rebase"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-10"&gt;Rebase&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Пересаживает коммит/ветку на нового родителя. Изменяет историю!&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;rebase&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;-i&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;target-ref&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Интерактивный ребейз - очень мощная штука.
Позволяет выбрать какие коммиты и в каком порядке пересаживать,
слепливать несколько коммитов в один, изменять их содержимое и мессаджи.&lt;/p&gt;
&lt;p&gt;Конфликты возможны на каждом пересаживаемом патче.&lt;/p&gt;
&lt;p&gt;В случае мерж конфликтов при ребейзе &lt;tt class="docutils literal"&gt;LOCAL&lt;/tt&gt; относится к тому на что
ребейзится, а &lt;tt class="docutils literal"&gt;REMOTE&lt;/tt&gt; - то что ребейзится.
Это иногда не интуитивно - например, у вас был старый master,
от которого отбранчевана feature-branch.
Вы обновили мастер и пытаетесь ребейзнуть свой feature-branch на него.
С первого взгляда интуитивно кажется что LOCAL - это ваш код
(вот, же он, локальный, еще может даже никуда не выложеный),
а REMOTE - это тот код из master, что вы только что выкачали
(из удаленного же хранилища, да?...).
Надо просто запомнить.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="section-5"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-11"&gt;Правки и изменения истории&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;В &lt;tt class="docutils literal"&gt;git&lt;/tt&gt; есть несколько команд, которые изменяют историю коммитов,
как минимум с точки зрения внешнего мира (список не полный):&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;git commit &lt;span class="pre"&gt;--amend&lt;/span&gt;&lt;/tt&gt; - дополняет последний коммит изменениями
находящимися в индексе. Поскольку id коммита определяется его содержимым,
изменяет id коммита&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;git rebase&lt;/tt&gt; - так как id коммита определяется также и id его родителей,
ребейз меняет id &lt;em&gt;всех&lt;/em&gt; коммитов попавших под ребейз.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Что тут надо учитывать:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Выложить такие изменения в уже существющую удаленную ветку на remote вы уже
просто так не сможете - история разъехалась&lt;ul&gt;
&lt;li&gt;Заставить выложить можно через &lt;tt class="docutils literal"&gt;git push &lt;span class="pre"&gt;--force&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Но теперь при этом в вашей удаленной ветке тоже изменилась история.
И теперь у любого, кто уже успел скачать через &lt;tt class="docutils literal"&gt;pull/fetch&lt;/tt&gt; себе вашу
удаленную ветку при попытке обновления возникнут проблемы -
их &amp;quot;локальная&amp;quot; история разошлась с &amp;quot;апстримной&amp;quot;, а это значит мерж-конфликты
и прочая головная боль на пустом месте (они-то свой код не трогали..).
Не надо так с людьми обходиться.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Поэтому общее правило для такого рода изменений -
они только для локальных коммитов.
Как только коммит в составе ветки выложен на удаленный репозиторий,
откуда эту ветку мог скачать кто-то ещё - переписывайте историю этой ветки
только будучи готовыми к лучам любви от этих людей.&lt;/p&gt;
&lt;p&gt;Но что примечательно - есть основаные на &lt;tt class="docutils literal"&gt;git&lt;/tt&gt; системы, очень активно
использующие &lt;tt class="docutils literal"&gt;amend&lt;/tt&gt; и &lt;tt class="docutils literal"&gt;rebase&lt;/tt&gt; в своей работе
(см &lt;a class="reference internal" href="#gerrit-workflow-openstack"&gt;Gerrit Workflow в OpenStack&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="tools"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-12"&gt;Tools&lt;/a&gt;&lt;/h3&gt;
&lt;div class="section" id="section-6"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="#toc-entry-13"&gt;Для любителей кнопочек и менюшечек&lt;/a&gt;&lt;/h4&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;gitk/git-gui - на лицо ужасный (Tcl/Tk),
добрый внутри “дефолтный” GUI для гита&lt;ul&gt;
&lt;li&gt;gitk - браузер истории&lt;/li&gt;
&lt;li&gt;git-gui - коммиты и проч.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;gitg - весьма пристойный аскетичный Гуй на Gtk&lt;/li&gt;
&lt;li&gt;git-cola - тоже неплохо,
прикольная визуализация DAG (directed acyclic graph) дерева коммитов&lt;/li&gt;
&lt;li&gt;SourceTree - для Win/Mac,
не открытый но бесплатный, от Atlassian, на Яблоке красивый&lt;/li&gt;
&lt;li&gt;Ваш IDE - наверное то же что-то есть (PyCharm, Eclipse+PyDev…)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="section-7"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="#toc-entry-14"&gt;Для ковбоев консоли&lt;/a&gt;&lt;/h4&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Git :)&lt;/li&gt;
&lt;li&gt;tig - браузер, коммитер, диффер и проч на ncurses. Пользуюсь постоянно.&lt;/li&gt;
&lt;li&gt;Vim плагины (у меня на нем профдеформация)&lt;ul&gt;
&lt;li&gt;Vim-fugitive - весьма мощная штука, но пока я не очень пользуюсь,
только для сложных интерактивных add. Надо переползать плотнее…&lt;/li&gt;
&lt;li&gt;Vim-gitgutter - помечает добавленые/удаленные/измененные строчки,
и может стейжить ханки.
Так же интегрируется в vim-airline и показывает общее количество
незакоммиченых изменений в открытом файле.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="fun"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="#toc-entry-15"&gt;Fun&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;gource - визуализация развития гит репозитория в динамике. Просто красиво&amp;nbsp;:)&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sudo apt install gource &amp;amp;&amp;amp; cd &amp;lt;repo&amp;gt; &amp;amp;&amp;amp; gource
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="gerrit-workflow-openstack"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-16"&gt;Gerrit Workflow в OpenStack&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Gerrit - система код-ревью основанная на Гите.&lt;/p&gt;
&lt;p&gt;В cвое время отфоркался от Rietveld написанного Гуидо ван Россумом,
создателем Python.&lt;/p&gt;
&lt;p&gt;Основной принцип - содержит ченжи, внутри каждого патч-сеты.
Каждый патч-сет - это отдельный бранч.
Это позволяет вовсю пользоваться rebase и commit --amend,
перезаписывая локальную историю и выкладывая ее на remote,
&lt;em&gt;что в общем случае очень сильно не рекомендуется&lt;/em&gt;
(см &lt;a class="reference internal" href="#section-5"&gt;Правки и изменения истории&lt;/a&gt;).&lt;/p&gt;
&lt;div class="section" id="openstack-english"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-17"&gt;Специфика сообщества OpenStack (english)&lt;/a&gt;&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;Общее описание Development Workflow&lt;/dt&gt;
&lt;dd&gt;&lt;a class="reference external" href="http://docs.openstack.org/infra/manual/developers.html#development-workflow"&gt;http://docs.openstack.org/infra/manual/developers.html#development-workflow&lt;/a&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;li&gt;&lt;dl class="first docutils"&gt;
&lt;dt&gt;Как писать годный коммит-мессадж&lt;/dt&gt;
&lt;dd&gt;&lt;a class="reference external" href="https://wiki.openstack.org/wiki/GitCommitMessages"&gt;https://wiki.openstack.org/wiki/GitCommitMessages&lt;/a&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="git-review"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-18"&gt;git-review&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;В принципе c Герритом можно работать через Git напрямую,
но с &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git-review&lt;/span&gt;&lt;/tt&gt; значительно удобнее.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;-H&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-U&lt;span class="w"&gt; &lt;/span&gt;git-review
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Стоит почитать&amp;nbsp;&lt;tt class="docutils literal"&gt;man &lt;span class="pre"&gt;git-review&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Настраивается через &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git-config&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;~/.gitconfig
…
&lt;span class="o"&gt;[&lt;/span&gt;gitreview&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;my-gerrit-user-name&amp;gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;rebase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="basic-workflow"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-19"&gt;Basic workflow&lt;/a&gt;&lt;/h3&gt;
&lt;div class="section" id="change-id"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="#toc-entry-20"&gt;Change-Id&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git-review&lt;/span&gt;&lt;/tt&gt; добавляет пост-коммит хук,
который добавляет в коммит-мессадж строчку (если её ещё там не было):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Change-Id: INNNNNN…
&lt;/pre&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Change-Id&lt;/dt&gt;
&lt;dd&gt;независимый, Gerrit-specific хэш, по которому Геррит определяет
в какой change ему добавить новую версию коммита.&lt;/dd&gt;
&lt;dt&gt;Очень важно&lt;/dt&gt;
&lt;dd&gt;не изменять строчку c Change-Id при обновлении патчей.
Новый Change-Id =&amp;gt; новый change на Геррите.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="section-8"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="#toc-entry-21"&gt;Работа над новым, независимым изменением (баг, фича)&lt;/a&gt;&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# git clone … &amp;amp;&amp;amp; cd &amp;lt;repo&amp;gt;&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;review&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# создает новый remote по имени gerrit&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;master
&lt;span class="c1"&gt;# git pull origin master&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;new-feature-branch&amp;gt;
&lt;span class="c1"&gt;# hack on it&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;.
git&lt;span class="w"&gt; &lt;/span&gt;commit
&lt;span class="c1"&gt;# проверяем что же мы закоммитили&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;log&lt;span class="w"&gt; &lt;/span&gt;-1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;diff&lt;span class="w"&gt; &lt;/span&gt;HEAD^..HEAD
&lt;span class="c1"&gt;# прогоняем юнит и прочие тесты&lt;/span&gt;
&lt;span class="c1"&gt;# tox [-e…]&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;review
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="section-9"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="#toc-entry-22"&gt;Правим свой старый патч&lt;/a&gt;&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# если ветки нет - скачиваем ее с Gerrit:&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;review&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;NNNNNN&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# review.openstack.org/#/c/NNNNNN&lt;/span&gt;
&lt;span class="c1"&gt;# создалась новая ветка review/&amp;lt;user_name&amp;gt;/&amp;lt;topic&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;# если ветка уже есть - переключаемся на нее:&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;feature-branch&amp;gt;
&lt;span class="c1"&gt;# если мерж конфликт&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;master
git&lt;span class="w"&gt; &lt;/span&gt;pull&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;master
git&lt;span class="w"&gt; &lt;/span&gt;fetch&lt;span class="w"&gt; &lt;/span&gt;gerrit
git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;feature-branch&amp;gt;
git&lt;span class="w"&gt; &lt;/span&gt;rebase&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;master
&lt;span class="c1"&gt;# и резолвить конфликты&lt;/span&gt;
&lt;span class="c1"&gt;# hack on it, address reviewers comments&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;.
&lt;span class="c1"&gt;# не создаем новый коммит!&lt;/span&gt;
&lt;span class="c1"&gt;# а добавляем изменения из индекса в последний коммит&lt;/span&gt;
&lt;span class="c1"&gt;# (и естесственно при этом меняет его commid-id)&lt;/span&gt;
git&lt;span class="w"&gt; &lt;/span&gt;commit&lt;span class="w"&gt; &lt;/span&gt;--amend
git&lt;span class="w"&gt; &lt;/span&gt;review
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Всегда коммитим обновления через аменд, перезаписывая последний коммит.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="rebase-or-not-rebase"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-23"&gt;Rebase or not Rebase?&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;По дефолту, при выкладывании через &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;git-review&lt;/span&gt;&lt;/tt&gt;, его &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pre-push&lt;/span&gt; hook&lt;/tt&gt;
попытается сделать ребейз вашего change на ту ветку в которую вы выкладываете
(по дефолту master).&lt;/p&gt;
&lt;p&gt;Иногда это хорошо (вы забыли обновить мастер, и теперь есть мерж-конфликты -
упадет сразу, не выложив).&lt;/p&gt;
&lt;p&gt;Это поведение отменяется ключом &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-R&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Но чаще всего лучше делать осознанный ребейз руками - ревьюерам вашего кода
проще сравнивать разные версии патч-сетов когда между ними не было ребейза.&lt;/p&gt;
&lt;p&gt;Отдельная история - ваш change зависит от чужого, еще не вмерженого и
находящегося на review.
В таком случае не ребейзить чужие патчи - общее правило хорошего тона.&lt;/p&gt;
&lt;p&gt;Поэтому мой личный алгоритм:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Отключить авторебейз по дефолту&lt;/li&gt;
&lt;li&gt;Новый change - всегда от мастера.&lt;/li&gt;
&lt;li&gt;Обновляю &lt;em&gt;свой, независимый&lt;/em&gt; старый change - ребейз только если:&lt;ul&gt;
&lt;li&gt;еще не было ни одного ревью, или&lt;/li&gt;
&lt;li&gt;если merge conflict (тут уж без вариантов)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;В остальных случаях - без ребейза.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="work"></category><category term="git"></category><category term="gerrit"></category><category term="openstack"></category></entry><entry><title>Single Russian-Ukrainian keyboard layout on Ubuntu</title><link href="https://pshchelo.github.io/ubuntu-ruu-kbd.html" rel="alternate"></link><published>2014-12-20T00:00:00+02:00</published><updated>2014-12-20T00:00:00+02:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2014-12-20:/ubuntu-ruu-kbd.html</id><summary type="html">&lt;p class="first last"&gt;Using single keyboard layout to input both Russian and Ukrainian in Ubuntu&lt;/p&gt;
</summary><content type="html">&lt;p&gt;My most used input language is English, with second used being Russian.
Occasionally though I have to input text (e.g. in search field) in German,
French or Ukrainian.
For Western languages Ubuntu comes with &amp;quot;English (international with AltGr dead keys)&amp;quot;
available as a choice of input layout.
It works out nicely as my default English layout,
and those extra accented letters are easy within reach
(plus some extra handy symbols are made available).
On the Cyrillic languages side things are a bit more complicated.
I could not find any suitable layout in Ubuntu enabled out of the box.&lt;/p&gt;
&lt;p&gt;It turns out there is one layout included in Ubuntu, one just has to manually turn it on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;open file &lt;tt class="docutils literal"&gt;/usr/share/X11/xkb/rules/evdev.extras.xml&lt;/tt&gt;, find the layout named &lt;tt class="docutils literal"&gt;ruu&lt;/tt&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;ruu&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;shortDescription&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/shortDescription&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="w"&gt; &lt;/span&gt;(with&lt;span class="w"&gt; &lt;/span&gt;Ukrainian-Belorussian&lt;span class="w"&gt; &lt;/span&gt;layout)&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;languageList&amp;gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;rus&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;ukr&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;bel&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&amp;lt;/languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/configItem&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/variant&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;copy the whole enclosing &lt;tt class="docutils literal"&gt;&amp;lt;variant&amp;gt;&lt;/tt&gt; tag as shown above&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;open file &lt;tt class="docutils literal"&gt;/usr/share/X11/xkb/rules/evdev.xml&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;find Russian section&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;layout&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;shortDescription&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/shortDescription&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;rus&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variantList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;phonetic&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="w"&gt; &lt;/span&gt;(phonetic)&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;paste there copied &lt;tt class="docutils literal"&gt;ruu&lt;/tt&gt; variant alongside other available variants&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;layout&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;shortDescription&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/shortDescription&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;rus&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variantList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;ruu&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;shortDescription&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/shortDescription&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="w"&gt; &lt;/span&gt;(with&lt;span class="w"&gt; &lt;/span&gt;Ukrainian-Belorussian&lt;span class="w"&gt; &lt;/span&gt;layout)&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;languageList&amp;gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;rus&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;ukr&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;bel&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&amp;lt;/languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;phonetic&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="w"&gt; &lt;/span&gt;(phonetic)&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;restart X server&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now you can choose a &amp;quot;Russian (with Ukrainian-Belorussian layout)&amp;quot; from available layouts.
It is mostly a standard Russian layout (the biggest difference being position of &amp;quot;ё&amp;quot; character),
plus you can input Ukrainian- or Belorussian-specific characters via AltGr
(usually right Alt), plus some more extra symbols are available via AltGr,
like Ukrainian Hryvnia (₴) or some math symbols (e.g. ±).
Check out the &amp;quot;Keyboard Layout Chart&amp;quot; for this layout to see all available characters.&lt;/p&gt;
</content><category term="work"></category><category term="keyboard"></category><category term="ukraine"></category><category term="ubuntu"></category></entry><entry><title>Единая русско-украинская раскладка в Ubuntu</title><link href="https://pshchelo.github.io/ubuntu-ruu-kbd-ru.html" rel="alternate"></link><published>2014-12-20T00:00:00+02:00</published><updated>2014-12-20T00:00:00+02:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2014-12-20:/ubuntu-ruu-kbd-ru.html</id><summary type="html">&lt;p class="first last"&gt;Using single keyboard layout to input both Russian and Ukrainian in Ubuntu&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Так получилось, что моим основным языком ввода является английский,
вторым по частоте использования русский.
Тем не менее, иногда мне надо вводить текст на немецком, франзузском и украинском языках.
Для западноевропейских языков в Ubuntu есть удобная раскладка
&amp;quot;English (international with AltGr dead keys)&amp;quot;.
Пользоваться ей как основной довольно удобно,
поскольку все акценты/умляуты доступны через AltGr,
плюс к ним несколько других символов.
Для кириллических языков ситуация несколько более сложная.
Я не смог найти подходящую раскладку в Ubuntu &amp;quot;из коробки&amp;quot;.&lt;/p&gt;
&lt;p&gt;Как оказалось, раскладка-то есть, но ее надо специально руками включить:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;открыть файл &lt;tt class="docutils literal"&gt;/usr/share/X11/xkb/rules/evdev.extras.xml&lt;/tt&gt;, найти вариант раскладки с именем &lt;tt class="docutils literal"&gt;ruu&lt;/tt&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;ruu&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;shortDescription&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/shortDescription&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="w"&gt; &lt;/span&gt;(with&lt;span class="w"&gt; &lt;/span&gt;Ukrainian-Belorussian&lt;span class="w"&gt; &lt;/span&gt;layout)&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;languageList&amp;gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;rus&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;ukr&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;bel&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&amp;lt;/languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/configItem&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/variant&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;скопировать всю часть между тэгами &lt;tt class="docutils literal"&gt;&amp;lt;variant&amp;gt;&lt;/tt&gt; как показано выше&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;открыть файл &lt;tt class="docutils literal"&gt;/usr/share/X11/xkb/rules/evdev.xml&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;найти секцию описывающую Русский язык&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;layout&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;shortDescription&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/shortDescription&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;rus&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variantList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;phonetic&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="w"&gt; &lt;/span&gt;(phonetic)&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;вставить ранее скопированный вариант раскладки &lt;tt class="docutils literal"&gt;ruu&lt;/tt&gt; рядом с другими доступными вариантами&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;layout&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;shortDescription&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/shortDescription&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;rus&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variantList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;ruu&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;shortDescription&amp;gt;&lt;/span&gt;ru&lt;span class="nt"&gt;&amp;lt;/shortDescription&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="w"&gt; &lt;/span&gt;(with&lt;span class="w"&gt; &lt;/span&gt;Ukrainian-Belorussian&lt;span class="w"&gt; &lt;/span&gt;layout)&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;languageList&amp;gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;rus&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;ukr&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;iso639Id&amp;gt;&lt;/span&gt;bel&lt;span class="nt"&gt;&amp;lt;/iso639Id&amp;gt;&amp;lt;/languageList&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;configItem&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;phonetic&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Russian&lt;span class="w"&gt; &lt;/span&gt;(phonetic)&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;перезапустить X server&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Теперь в списке доступных раскладок появляется &amp;quot;Russian (with Ukrainian-Belorussian layout)&amp;quot;.
В основном это стандартная русская раскладка (главное отличие - расплолжение буквы ё),
но все специфично украинские и белорусские буквы доступны через AltGr,
и кроме них несколько других плюшек вроде валютного значка
украинской гривны (₴) или всяких математических знаков (например ±).
Чтобы увидеть все доступные символы, проверьте &amp;quot;Keyboard Layout Chart&amp;quot; для этой раскладки.&lt;/p&gt;
</content><category term="work"></category><category term="keyboard"></category><category term="ukraine"></category><category term="ubuntu"></category></entry><entry><title>How to set DevStack with Neutron</title><link href="https://pshchelo.github.io/devstack-with-neutron.html" rel="alternate"></link><published>2014-04-21T00:00:00+03:00</published><updated>2014-04-21T00:00:00+03:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2014-04-21:/devstack-with-neutron.html</id><summary type="html">&lt;p class="first last"&gt;tips and tricks on setting DevStack&lt;/p&gt;
</summary><content type="html">&lt;p&gt;These tips try to solve problems I stumbled upon when starting my work
on OpenStack, Grizzly release at that time. I've tried to accommodate
the solutions for recent DevStack (early Juno as of this
writing), but some things might still be incorrect or already fixed -
YMMV.&lt;/p&gt;
&lt;p&gt;All notes are concerning Ubuntu/Debian on an Intel CPU, so for other systems
some changes might be necessary.&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Update&lt;/dt&gt;
&lt;dd&gt;Tried DevStack on fresh Ubuntu 14.04 LTS (Trusty Thar), updated tips.&lt;/dd&gt;
&lt;/dl&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#install-devstack" id="toc-entry-1"&gt;Install DevStack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#allow-vm-internet-connectivity" id="toc-entry-2"&gt;Allow VM Internet connectivity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#note-on-unstack-sh-and-rebooting" id="toc-entry-3"&gt;Note on &lt;tt class="docutils literal"&gt;unstack.sh&lt;/tt&gt; and rebooting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#restore-settings-after-reboot" id="toc-entry-4"&gt;Restore settings after reboot&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#stack-volumes" id="toc-entry-5"&gt;Stack volumes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#nat-settings" id="toc-entry-6"&gt;NAT settings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#bridges" id="toc-entry-7"&gt;Bridges&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#sharing-files-between-your-physical-machine-and-devstack-host" id="toc-entry-8"&gt;Sharing files between your physical machine and DevStack host&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#problems-with-receiving-ips-for-vms" id="toc-entry-9"&gt;Problems with receiving IPs for VMs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#nested-kvm" id="toc-entry-10"&gt;Nested KVM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="install-devstack"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-1"&gt;Install DevStack&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Prepare a VM for DevStack deployment&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;in this VM install &lt;tt class="docutils literal"&gt;git&lt;/tt&gt;&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sudo apt-get install git
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;clone DevStack repo&lt;/p&gt;
&lt;pre class="literal-block"&gt;
git clone https://github.com/openstack-dev/devstack.git
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;create &lt;tt class="docutils literal"&gt;local.conf&lt;/tt&gt; file in the &lt;tt class="docutils literal"&gt;devstack&lt;/tt&gt; folder to enable
Neutron and set some passwords (choose your own if you wish)&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[[local|localrc]]
DATABASE_PASSWORD=password
RABBIT_PASSWORD=password
SERVICE_TOKEN=password
SERVICE_PASSWORD=password
ADMIN_PASSWORD=password
disable_service n-net
enable_service q-svc q-agt q-dhcp q-l3 q-meta neutron
&lt;/pre&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;I have a &lt;a class="reference external" href="https://github.com/pshchelo/stackdev"&gt;repo&lt;/a&gt; with my
DevStack customizations, you might want to check it out.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;install DevStack&lt;/p&gt;
&lt;pre class="literal-block"&gt;
./stack.sh
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now you should have a working DevStack installation. You can login into
Horizon, or join the running stack with &lt;tt class="docutils literal"&gt;./rejoin_stack.sh&lt;/tt&gt; command
and check console outputs of all the running services for debug and error info.
There are some networks and routers created by default, so you can
start VMs and attach floating IPs to them.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="allow-vm-internet-connectivity"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-2"&gt;Allow VM Internet connectivity&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You might stumble on networking issues concerning outside access,
like pinging or accessing Internet resources outside of your DevStack.
First you need to change the Security Group settings -
the &amp;quot;default&amp;quot; group created by DevStack seems to allow everything
but in fact I've always had problems with it, so just create your own
security group and assign your VMs to it.
Then you need to configure &lt;tt class="docutils literal"&gt;iptables&lt;/tt&gt; to pass through the traffic:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
&lt;/pre&gt;
&lt;p&gt;(after this &lt;a class="reference external" href="https://answers.launchpad.net/neutron/+question/208377"&gt;launchpad question&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="note-on-unstack-sh-and-rebooting"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-3"&gt;Note on &lt;tt class="docutils literal"&gt;unstack.sh&lt;/tt&gt; and rebooting&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I've found the hard way, if you ever need to reboot your DevStack VM,
&lt;strong&gt;do not&lt;/strong&gt; run &lt;tt class="docutils literal"&gt;unstack.sh&lt;/tt&gt; before that.
Simply detach from screen and/or reboot as usual.
The unstacking script not only stops the services run in screen,
it also alters some configuration options of your system
that were introduced by running &lt;tt class="docutils literal"&gt;stack.sh&lt;/tt&gt; and setting up the DevStack.
Mostly it concerns network bridges from what I have seen.
So if you run &lt;tt class="docutils literal"&gt;unstack.sh&lt;/tt&gt;, in order to have a working DevStack installation
you have to run &lt;tt class="docutils literal"&gt;stack.sh&lt;/tt&gt; after it.
That has a drawback that all your cloud configuration
(e.g. everything stored in DB tables of OpenStack and content of stack-volume-backing-file) will be reset.&lt;/p&gt;
&lt;p&gt;Thus, the work flow looks somewhat like this:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Start/configure DevStack with &lt;tt class="docutils literal"&gt;stack.sh&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Join the stack with &lt;tt class="docutils literal"&gt;rejoin_stack.sh&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Make changes, restarting services affected on per-service basis via screen
to put your changes in effect.&lt;/li&gt;
&lt;li&gt;If in need to reboot the DevStack VM, shut it down the usual way,
and upon restart simply run &lt;tt class="docutils literal"&gt;rejoin_stack.sh&lt;/tt&gt; again,
all services will be started in screen again.&lt;/li&gt;
&lt;li&gt;Only if you need to change the OpenStack/DevStack configuration,
then run &lt;tt class="docutils literal"&gt;unstack.sh&lt;/tt&gt;, make changes in &lt;tt class="docutils literal"&gt;local.conf&lt;/tt&gt; / wherever you have to
and then run &lt;tt class="docutils literal"&gt;stack.sh&lt;/tt&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="restore-settings-after-reboot"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-4"&gt;Restore settings after reboot&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After reboot of the DevStack VM the following will be lost:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;stack volumes (this includes created storage volumes etc.)&lt;/li&gt;
&lt;li&gt;iptables/NAT setting&lt;/li&gt;
&lt;li&gt;bridges configurations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here how to restore these settings or make them permanent:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Update&lt;/dt&gt;
&lt;dd&gt;Right now it appears to be so involved to cleanly restore all the settings
lost after reboot that a much safer and easier way is to make peace with
everything in your OpenStack being lost (all the settings and things
you've created inside) and just run &lt;tt class="docutils literal"&gt;stack.sh&lt;/tt&gt; again (optionally with
&lt;tt class="docutils literal"&gt;OFFLINE = True&lt;/tt&gt; in your &lt;tt class="docutils literal"&gt;local.conf&lt;/tt&gt; to keep the code exactly as
before reboot).&lt;/dd&gt;
&lt;/dl&gt;
&lt;div class="section" id="stack-volumes"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-5"&gt;Stack volumes&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - with &lt;tt class="docutils literal"&gt;Swift&lt;/tt&gt; enabled actual loopback device may be other
than &lt;tt class="docutils literal"&gt;/dev/loop0&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Check that volumes are created after fresh running of &lt;tt class="docutils literal"&gt;./stack.sh&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sudo losetup -a
sudo pvs
sudo vgs
&lt;/pre&gt;
&lt;p&gt;You should see volume group &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;stack-volumes&lt;/span&gt;&lt;/tt&gt; existing and attached to
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/opt/stack/data/stack-volumes-backing-file&lt;/span&gt;&lt;/tt&gt; via &lt;tt class="docutils literal"&gt;/dev/loop0&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;After reboot the attachment of the backing file to loopback device will
be lost. To make it permanent add the following line to your
&lt;tt class="docutils literal"&gt;/etc/rc.local&lt;/tt&gt; file, before &lt;tt class="docutils literal"&gt;exit 0&lt;/tt&gt; line:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
losetup /dev/loop0 /opt/stack/data/stack-volumes-backing-file
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="nat-settings"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-6"&gt;NAT settings&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After &lt;a class="reference external" href="https://lists.launchpad.net/openstack/msg17016.html"&gt;this mail-list
post&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;permanently enable IP-forwarding&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sudo sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;permanently set &lt;tt class="docutils literal"&gt;iptables&lt;/tt&gt; settings: add to &lt;tt class="docutils literal"&gt;/etc/interfaces&lt;/tt&gt;&lt;/p&gt;
&lt;pre class="literal-block"&gt;
post up iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="bridges"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-7"&gt;Bridges&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After &lt;a class="reference external" href="https://gist.github.com/charlesflynn/5576114"&gt;this ghist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Bridge &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;br-ex&lt;/span&gt;&lt;/tt&gt; has no IP after reboot.
Use the following commands to set bridges according to default settings
of &lt;tt class="docutils literal"&gt;stack.sh&lt;/tt&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class="literal-block"&gt;
sudo ip addr flush dev br-ex
sudo ip addr add 172.24.4.1/24 dev br-ex
sudo ip link set br-ex up
sudo route add -net 10.0.0.0/24 gw 172.24.4.1
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="sharing-files-between-your-physical-machine-and-devstack-host"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-8"&gt;Sharing files between your physical machine and DevStack host&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I use &lt;tt class="docutils literal"&gt;vim&lt;/tt&gt; as my Python IDE I personally prefer to work directly
in the console of the guest DevStack instance, but if you prefer GUI IDE
(like PyCharm) you might want to have access to the code on the DevStack
guest right from your host. One rather straightforward possibility is
&lt;tt class="docutils literal"&gt;sshfs&lt;/tt&gt;, but as it is usually pretty slow, you might want to try NFS.&lt;/p&gt;
&lt;p&gt;The following is adopted from &lt;a class="reference external" href="http://radix.twistedmatrix.com/2013/06/complete-guide-to-setting-up-openstack.html"&gt;this post by
radix&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;First, install the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nfs-kernel-server&lt;/span&gt;&lt;/tt&gt; package on the host system and
then edit &lt;tt class="docutils literal"&gt;/etc/exports&lt;/tt&gt; to add the following line:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
full_path_to_project_on_host    *(rw,async,root_squash,no_subtree_check)
&lt;/pre&gt;
&lt;p&gt;Then in the DevStack guest install &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;nfs-common&lt;/span&gt;&lt;/tt&gt; and add the following
line to &lt;tt class="docutils literal"&gt;/etc/fstab&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
address_of_host:full_path_to_project_on_host    full_path_to_project_on_guest    nfs
&lt;/pre&gt;
&lt;p&gt;Don't forget to &lt;tt class="docutils literal"&gt;mkdir full_path_to_project_on_guest&lt;/tt&gt; in the guest.
You can then reboot the DevStack guest or just mount
&lt;tt class="docutils literal"&gt;full_path_to_project_on_guest&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="problems-with-receiving-ips-for-vms"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-9"&gt;Problems with receiving IPs for VMs&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some versions of VirtIO network interface seem not to fill in checksums correctly in UDP
packets (something called checksum offloading), which interferes with
receiving DHCP lease from neutron/nova-network when everything is
running on a single host (e.g. DevStack). To fix this add the following
rule to &lt;tt class="docutils literal"&gt;iptables&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sudo iptables -A POSTROUTING -t mangle -p udp --dport bootpc -j CHECKSUM --checksum-fill
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="nested-kvm"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-10"&gt;Nested KVM&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you run DevStack as a KVM guest, ensure that your host system has nested KVM enabled -
that will greatly speed up those VMs you run inside your DevStack guest
(of course your CPU has to support virtualization extensions and have them enabled in BIOS).&lt;/p&gt;
&lt;p&gt;Check that nested KVM is enabled:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
cat /sys/module/kvm_intel/parameters/nested
&lt;/pre&gt;
&lt;p&gt;If it's &lt;tt class="docutils literal"&gt;N&lt;/tt&gt; then try to load the module with&lt;/p&gt;
&lt;pre class="literal-block"&gt;
modprobe kvm_intel nested=1
&lt;/pre&gt;
&lt;p&gt;If it worked (you get &lt;tt class="docutils literal"&gt;Y&lt;/tt&gt; after checking again) to make it permanent
you have to add the following line to some &lt;tt class="docutils literal"&gt;.conf&lt;/tt&gt; file in &lt;tt class="docutils literal"&gt;/etc/modprobe.d/&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
options kvm-intel nested=1
&lt;/pre&gt;
&lt;p&gt;Reboot and check again.&lt;/p&gt;
&lt;/div&gt;
</content><category term="work"></category><category term="devstack"></category><category term="openstack"></category></entry><entry><title>UEFI boot order on dualbooted HP EliteBook 840</title><link href="https://pshchelo.github.io/uefi-boot-hp.html" rel="alternate"></link><published>2014-04-14T00:00:00+03:00</published><updated>2014-04-14T00:00:00+03:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2014-04-14:/uefi-boot-hp.html</id><summary type="html">&lt;p class="first last"&gt;setting up dualbooted HP notebook in UEFI mode&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="problem"&gt;
&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;Using UEFI+SecureBoot, dual boot Windows 8.1 / Ubuntu 13.10. Boot order
can be configured in BIOS per device (HDD/ODD/PXI/USB etc) but there is
no way to specify boot order of systems present on HDD. UEFI boot loader
finds both Windows (present as &lt;tt class="docutils literal"&gt;OS Manager&lt;/tt&gt;) and Ubuntu (as
&lt;tt class="docutils literal"&gt;ubuntu&lt;/tt&gt;), but always uses &lt;tt class="docutils literal"&gt;OS Manager&lt;/tt&gt; first. As I intend to use
Ubuntu most of the time, I need a way to make booting Ubuntu the default
option.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="research"&gt;
&lt;h2&gt;Research&lt;/h2&gt;
&lt;p&gt;According to &lt;em&gt;teh Internets&lt;/em&gt; such problems seem to occur reasonably
often, when vendors &amp;quot;under-implement&amp;quot; UEFI on their products. One
commonly proposed solution is using recent versions of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;boot-repair&lt;/span&gt;&lt;/tt&gt;
Ubuntu package (a really nice piece of engineering, saved my ass couple
of times in the past). What it does is it substitutes (with backup of
course) the windows &lt;em&gt;efi&lt;/em&gt; file with accordingly renamed Ubuntu &lt;em&gt;efi&lt;/em&gt;
file, thus loading GRUB automatically, and from there user can choose
what system to boot. But I find such solution a bit too risky, so I want
something more native.&lt;/p&gt;
&lt;p&gt;Another program that is related is &lt;tt class="docutils literal"&gt;efibootmgr&lt;/tt&gt;&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sudo apt-get install efibootmgr
&lt;/pre&gt;
&lt;p&gt;It has the option to modify boot order of &lt;em&gt;efi&lt;/em&gt; files with
&lt;tt class="docutils literal"&gt;efibootmgr &lt;span class="pre"&gt;-o&lt;/span&gt;&lt;/tt&gt;, but at least on my particular HP notebook it has no
effect, and system boots to Windows by default despite any changes in
the boot order introduced by &lt;tt class="docutils literal"&gt;efibootmgr&lt;/tt&gt;. Strangely though, the
option to force the boot order only on next reboot (&lt;tt class="docutils literal"&gt;efibootmgr &lt;span class="pre"&gt;-n&lt;/span&gt;&lt;/tt&gt;)
is working alright. So, here comes the solution I figured out with the
help of &lt;a class="reference external" href="http://ubuntuforums.org/showthread.php?t=2173267"&gt;this Ubuntu Forums
thread&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="solution"&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;My current EFI setup looks like following&lt;/p&gt;
&lt;pre class="literal-block"&gt;
shell$: sudo efibootmgr
BootCurrent: 0001
Timeout: 0 seconds
BootOrder: 0001,0002
Boot0001* ubuntu
Boot0002* Windows Boot Manager
&lt;/pre&gt;
&lt;p&gt;So I 've placed the following line in &lt;tt class="docutils literal"&gt;/etc/rc.local&lt;/tt&gt; (before the
final &lt;tt class="docutils literal"&gt;exit 0&lt;/tt&gt; line):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
/bin/efibootmgr -n 0001
&lt;/pre&gt;
&lt;p&gt;This forces next default boot to be Ubuntu on every boot of Ubuntu. The
dark side - after booting to Windows, this setting naturally disappears,
so the next booted by default system after Windows boot is Windows
again. But actually this is &lt;strong&gt;exactly what I want&lt;/strong&gt; - it sort of
replicates &lt;tt class="docutils literal"&gt;GRUB_SAVE_DEFAULT&lt;/tt&gt; feature which I was always using
before, so that every next boot by default is the same as previous boot.
Installing zero delay on UEFI bootloader (there is still couple of
seconds to press F9 and go into UEFI boot menu manually), and very short
delay on GRUB (2 sec just for an emergency need to boot not into
standard Ubuntu) gives me a system that's fast enough to boot in
unattended mode.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="update"&gt;
&lt;h2&gt;Update&lt;/h2&gt;
&lt;p&gt;After living with this configuration for several months I realized two things:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;I do not use Windows more than once a month;&lt;/li&gt;
&lt;li&gt;Using hibernate via TuxOnIce has some problems.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last one is due to rc.local not being processed on resume from hibernate,
and unfortunately I failed to configure TuxOnIce to execute the &lt;tt class="docutils literal"&gt;efibootmgr&lt;/tt&gt;
properly on resume.&lt;/p&gt;
&lt;p&gt;I again turned to BIOS settings and this time noticed that I can
in fact configure &amp;quot;Custom&amp;quot; boot section and make it the first one to boot.
So now I have Ubuntu booting by default, and from Grub I can start Windows
if I need, and also I can safely use SAVE_DEFAULT option of Grub.
For those times when Windows &lt;em&gt;has&lt;/em&gt; to be booted directly from EFI,
there is still possibility to override first boot option during POST.&lt;/p&gt;
&lt;/div&gt;
</content><category term="work"></category><category term="hp"></category><category term="dualboot"></category><category term="uefi"></category></entry><entry><title>Solving the Rubik's Cube</title><link href="https://pshchelo.github.io/rubik-solve.html" rel="alternate"></link><published>2014-04-03T00:00:00+03:00</published><updated>2014-04-03T00:00:00+03:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2014-04-03:/rubik-solve.html</id><summary type="html">&lt;p class="first last"&gt;Algorithm for solving Rubik's Cube.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;See &lt;a class="reference external" href="https://pshchelo.github.io/rubik-notation.html"&gt;notation&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="cross-of-the-first-layer"&gt;
&lt;h2&gt;Cross of the first layer&lt;/h2&gt;
&lt;p&gt;Moves cube FD -&amp;gt; FU&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;F-&amp;gt;F, D-&amp;gt;U&lt;/dt&gt;
&lt;dd&gt;F&lt;sup&gt;2&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;F-&amp;gt;U, D-&amp;gt;F&lt;/dt&gt;
&lt;dd&gt;D R F' R'
or
F' M'&lt;sub&gt;D&lt;/sub&gt; F M&lt;sub&gt;D&lt;/sub&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="corners-of-the-first-layer"&gt;
&lt;h2&gt;Corners of the first layer&lt;/h2&gt;
&lt;p&gt;Moves cube FDL -&amp;gt; FUL&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;F-&amp;gt;F, D-&amp;gt;L, L-&amp;gt;U&lt;/dt&gt;
&lt;dd&gt;L D L'&lt;/dd&gt;
&lt;dt&gt;F-&amp;gt;U, D-&amp;gt;F, L-&amp;gt;L&lt;/dt&gt;
&lt;dd&gt;F' U' F&lt;/dd&gt;
&lt;dt&gt;F-&amp;gt;L, D-&amp;gt;U, L-&amp;gt;F&lt;/dt&gt;
&lt;dd&gt;( F' R' ) D&lt;sup&gt;2&lt;/sup&gt; ( R F )&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="second-layer"&gt;
&lt;h2&gt;Second layer&lt;/h2&gt;
&lt;p&gt;Moves cube FD&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;F-&amp;gt;F, D-&amp;gt;L&lt;/dt&gt;
&lt;dd&gt;( D L D' L' ) ( D' F' D F )&lt;/dd&gt;
&lt;dt&gt;F-&amp;gt;F, D-&amp;gt;R&lt;/dt&gt;
&lt;dd&gt;( D' R' D R ) ( D F D' F' )&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="placing-side-cubes-of-third-layer"&gt;
&lt;h2&gt;Placing side cubes of third layer&lt;/h2&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Moves cube FU -&amp;gt; LU&lt;/dt&gt;
&lt;dd&gt;(U F R ) U (R' U' F' )&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="cross-of-the-third-layer"&gt;
&lt;h2&gt;Cross of the third layer&lt;/h2&gt;
&lt;p&gt;Rotating cube UR&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Rotate UR and UB&lt;/dt&gt;
&lt;dd&gt;( R M&lt;sub&gt;D&lt;/sub&gt; )&lt;sup&gt;4&lt;/sup&gt; U ( R M&lt;sub&gt;D&lt;/sub&gt; )&lt;sup&gt;4&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;Rotate UR and UF&lt;/dt&gt;
&lt;dd&gt;( R M&lt;sub&gt;D&lt;/sub&gt; )&lt;sup&gt;4&lt;/sup&gt; U' ( R M&lt;sub&gt;D&lt;/sub&gt; )&lt;sup&gt;4&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;Rotate UR and UL&lt;/dt&gt;
&lt;dd&gt;( R M&lt;sub&gt;D&lt;/sub&gt; )&lt;sup&gt;4&lt;/sup&gt; U&lt;sup&gt;2&lt;/sup&gt; ( R M&lt;sub&gt;D&lt;/sub&gt; )&lt;sup&gt;4&lt;/sup&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="placing-corner-cubes-of-third-layer"&gt;
&lt;h2&gt;Placing corner cubes of third layer&lt;/h2&gt;
&lt;p&gt;Moves cubes FLU, FLR, RUB, cube ULB is not moving&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Shift by one position clockwise&lt;/dt&gt;
&lt;dd&gt;( R' F' L' F ) (R F' L F )&lt;/dd&gt;
&lt;dt&gt;Shift by one position counter-clockwise&lt;/dt&gt;
&lt;dd&gt;( F' L' F R' ) ( F' L F R )&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="final-placement"&gt;
&lt;h2&gt;Final placement&lt;/h2&gt;
&lt;p&gt;Rotate the cube FUR&lt;/p&gt;
&lt;p&gt;[ ( R F' R' F )&lt;sup&gt;2&lt;/sup&gt; ]&lt;sup&gt;n&lt;/sup&gt; {U | U' | U&lt;sup&gt;2&lt;/sup&gt; } [ ( R F' R' F )&lt;sup&gt;2&lt;/sup&gt; ]&lt;sup&gt;n&lt;/sup&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;n&lt;/tt&gt; - rotates FUR by &lt;tt class="docutils literal"&gt;n/3&lt;/tt&gt; clockwise&lt;/li&gt;
&lt;li&gt;U - rotates FUR and RUB&lt;/li&gt;
&lt;li&gt;U' - rotates FUR and FUL&lt;/li&gt;
&lt;li&gt;U&lt;sup&gt;2&lt;/sup&gt; - rotates FUR and LUB&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><category term="play"></category><category term="puzzles"></category><category term="rubik"></category></entry><entry><title>Figures from the solved Rubik's Cube</title><link href="https://pshchelo.github.io/rubik-figures.html" rel="alternate"></link><published>2014-04-02T00:00:00+03:00</published><updated>2014-04-02T00:00:00+03:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2014-04-02:/rubik-figures.html</id><summary type="html">&lt;p class="first last"&gt;Figures from solved Rubik's Cube&lt;/p&gt;
</summary><content type="html">&lt;p&gt;See the &lt;a class="reference external" href="https://pshchelo.github.io/rubik-notation.html"&gt;notation&lt;/a&gt;.&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Donkey's Bridge (chess cube of the 2nd order)&lt;/dt&gt;
&lt;dd&gt;M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;D&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;Dots&lt;/dt&gt;
&lt;dd&gt;M'&lt;sub&gt;D&lt;/sub&gt; M'&lt;sub&gt;R&lt;/sub&gt; M&lt;sub&gt;D&lt;/sub&gt; M&lt;sub&gt;R&lt;/sub&gt;&lt;/dd&gt;
&lt;dt&gt;Christmann's cross&lt;/dt&gt;
&lt;dd&gt;R' (M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; D&lt;sup&gt;2&lt;/sup&gt;) R&lt;/dd&gt;
&lt;dt&gt;Plummer's cross&lt;/dt&gt;
&lt;dd&gt;G&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; [G&lt;sub&gt;F&lt;/sub&gt; (U&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; D' M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; ) ]&lt;sup&gt;2&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;Chess cube of the 3rd order (mix of Dots and Plumer's cross)&lt;/dt&gt;
&lt;dd&gt;[ ( M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; D M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U' M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U&lt;sup&gt;2&lt;/sup&gt; ) G'&lt;sub&gt;F&lt;/sub&gt; ]&lt;sup&gt;2&lt;/sup&gt;
G&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt;
(M'&lt;sub&gt;R&lt;/sub&gt; M'&lt;sub&gt;D&lt;/sub&gt; M&lt;sub&gt;R&lt;/sub&gt; M&lt;sub&gt;D&lt;/sub&gt;) G&lt;sub&gt;R&lt;/sub&gt; G'&lt;sub&gt;D&lt;/sub&gt; M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;D&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;Chess cube of the 6th order (mix of Donkey's Bridge and 3rd order chess cube)&lt;/dt&gt;
&lt;dd&gt;[ ( M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; D M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U' M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U&lt;sup&gt;2&lt;/sup&gt; ) G'&lt;sub&gt;F&lt;/sub&gt; ]&lt;sup&gt;2&lt;/sup&gt;
G&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt;
(M'&lt;sub&gt;R&lt;/sub&gt; M'&lt;sub&gt;D&lt;/sub&gt; M&lt;sub&gt;R&lt;/sub&gt; M&lt;sub&gt;D&lt;/sub&gt;) G&lt;sub&gt;R&lt;/sub&gt; G'&lt;sub&gt;D&lt;/sub&gt; M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;D&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;&amp;quot;6 H&amp;quot;&lt;/dt&gt;
&lt;dd&gt;D&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;R&lt;/sub&gt; M&lt;sub&gt;F&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M'&lt;sub&gt;R&lt;/sub&gt; U&lt;sup&gt;2&lt;/sup&gt; G&lt;sub&gt;D&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;&amp;quot;6 dashes&amp;quot;&lt;/dt&gt;
&lt;dd&gt;R&lt;sup&gt;2&lt;/sup&gt; F&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; B&lt;sup&gt;2&lt;/sup&gt; L&lt;sup&gt;2&lt;/sup&gt; G&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;R&lt;/sub&gt; G'&lt;sub&gt;R&lt;/sub&gt;&lt;/dd&gt;
&lt;dt&gt;&amp;quot;6 flags&amp;quot;&lt;/dt&gt;
&lt;dd&gt;U' B&lt;sup&gt;2&lt;/sup&gt; L&lt;sup&gt;2&lt;/sup&gt; U M&lt;sub&gt;R&lt;/sub&gt;&lt;sup&gt;2&lt;/sup&gt; U' R&lt;sup&gt;2&lt;/sup&gt; F&lt;sup&gt;2&lt;/sup&gt; D F B R M&lt;sub&gt;D&lt;/sub&gt; R' B' R' M&lt;sub&gt;D&lt;/sub&gt; R&lt;sup&gt;2&lt;/sup&gt; M'&lt;sub&gt;D&lt;/sub&gt; R' F' B&lt;sup&gt;2&lt;/sup&gt;(?) R&lt;sup&gt;2&lt;/sup&gt; B&lt;sup&gt;2&lt;/sup&gt; F&lt;sup&gt;2&lt;/sup&gt; M&lt;sub&gt;F&lt;/sub&gt; G&lt;sub&gt;F&lt;/sub&gt; G'&lt;sub&gt;D&lt;/sub&gt;&lt;/dd&gt;
&lt;dt&gt;Meson (quark-antiquark) - 2 diaginally opposite corner cubes are rotated&lt;/dt&gt;
&lt;dd&gt;L&lt;sup&gt;2&lt;/sup&gt; R' D R F D F' U' F D' F' R' D' R U L&lt;sup&gt;2&lt;/sup&gt;&lt;/dd&gt;
&lt;dt&gt;Giant meson - the same as Meson, but 2x2x2 cubes are rotated&lt;/dt&gt;
&lt;dd&gt;F' U' B U&lt;sup&gt;2&lt;/sup&gt; B' U R U&lt;sup&gt;2&lt;/sup&gt; R' F
B D F' D&lt;sup&gt;2&lt;/sup&gt; F D' L' D&lt;sup&gt;2&lt;/sup&gt; L B'&lt;/dd&gt;
&lt;dt&gt;Giant meson with cherries - mix of Meson and Giant meson, 2x2x2 cube is rotated but the corner is not&lt;/dt&gt;
&lt;dd&gt;R' U&lt;sup&gt;2&lt;/sup&gt; D B' M&lt;sub&gt;D&lt;/sub&gt; B&lt;sup&gt;2&lt;/sup&gt; M'&lt;sub&gt;D&lt;/sub&gt; B' U&lt;sup&gt;2&lt;/sup&gt; D' R
L D&lt;sup&gt;2&lt;/sup&gt; U' F M&lt;sub&gt;D&lt;/sub&gt; F&lt;sup&gt;2&lt;/sup&gt; M'&lt;sub&gt;D&lt;/sub&gt; F D&lt;sup&gt;2&lt;/sup&gt; U L'
M'&lt;sub&gt;D&lt;/sub&gt; M'&lt;sub&gt;R&lt;/sub&gt; M&lt;sub&gt;D&lt;/sub&gt; M&lt;sub&gt;R&lt;/sub&gt;&lt;/dd&gt;
&lt;dt&gt;Globe - no tile has a common border with a tile of the same color&lt;/dt&gt;
&lt;dd&gt;[FBLR]&lt;sup&gt;2&lt;/sup&gt;LR&lt;/dd&gt;
&lt;/dl&gt;
</content><category term="play"></category><category term="puzzles"></category><category term="rubik"></category></entry><entry><title>Rubik's Cube</title><link href="https://pshchelo.github.io/rubik-notation.html" rel="alternate"></link><published>2014-04-01T00:00:00+03:00</published><updated>2014-04-01T00:00:00+03:00</updated><author><name>pshchelo</name></author><id>tag:pshchelo.github.io,2014-04-01:/rubik-notation.html</id><summary type="html">&lt;p class="first last"&gt;Notation used in posts on Rubik's Cube&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="about"&gt;
&lt;h2&gt;About&lt;/h2&gt;
&lt;p&gt;Quite probably the best known puzzle in the world - cube consisting
of 3x3x3 matrix of colored cubes, that can be rotated per layer.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="notation-used"&gt;
&lt;h2&gt;Notation used&lt;/h2&gt;
&lt;p&gt;Choose the front face - the one facing you. Then the letters denote:&lt;/p&gt;
&lt;object class="align-center" data="https://pshchelo.github.io/images/rubik-files/rubik-rotations.svg" style="height: 300px;" type="image/svg+xml"&gt;Slice rotations&lt;/object&gt;
&lt;object class="align-center" data="https://pshchelo.github.io/images/rubik-files/rubik-3d.svg" style="height: 300px;" type="image/svg+xml"&gt;Whole cube rotations&lt;/object&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;F&lt;/dt&gt;
&lt;dd&gt;rotate the front face of the cube clock-wise&lt;/dd&gt;
&lt;dt&gt;B&lt;/dt&gt;
&lt;dd&gt;rotate the rear (back) face of the cube counter-clockwise (clockwise if you would make it front)&lt;/dd&gt;
&lt;dt&gt;R&lt;/dt&gt;
&lt;dd&gt;rotate the right face of the cube upward (clockwise if you would make it front front)&lt;/dd&gt;
&lt;dt&gt;L&lt;/dt&gt;
&lt;dd&gt;rotate the left face of the cube downward (i.e. also clockwise)&lt;/dd&gt;
&lt;dt&gt;U&lt;/dt&gt;
&lt;dd&gt;rotate the top (upper) face of the cube to the left (i.e. clockwise)&lt;/dd&gt;
&lt;dt&gt;D&lt;/dt&gt;
&lt;dd&gt;rotate the bottom (down) face of the cube to the right (i.e. clockwise)&lt;/dd&gt;
&lt;dt&gt;M&lt;sub&gt;R&lt;/sub&gt;&lt;/dt&gt;
&lt;dd&gt;rotate the middle slice of the cube upward (clockwise, as R turn)&lt;/dd&gt;
&lt;dt&gt;M&lt;sub&gt;D&lt;/sub&gt;&lt;/dt&gt;
&lt;dd&gt;rotate the middle slice to the right (clockwise, as D turn)&lt;/dd&gt;
&lt;dt&gt;M&lt;sub&gt;F&lt;/sub&gt;&lt;/dt&gt;
&lt;dd&gt;rotate the unseen middle slice clockwise (the slice between front and rear faces)&lt;/dd&gt;
&lt;dt&gt;G&lt;sub&gt;R&lt;/sub&gt;&lt;/dt&gt;
&lt;dd&gt;rotate the whole cube upward (clockwise, as R turn)&lt;/dd&gt;
&lt;dt&gt;G&lt;sub&gt;F&lt;/sub&gt;&lt;/dt&gt;
&lt;dd&gt;rotate the whole cube clockwise (as F turn)&lt;/dd&gt;
&lt;dt&gt;G&lt;sub&gt;D&lt;/sub&gt;&lt;/dt&gt;
&lt;dd&gt;rotate the whole cube to the right (as D turn)&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;The primed letter means change of direction to counterclockwise:
F', B', R', L', U', D', M'&lt;sub&gt;R&lt;/sub&gt;, M'&lt;sub&gt;D&lt;/sub&gt;, M'&lt;sub&gt;F&lt;/sub&gt;, G'&lt;sub&gt;R&lt;/sub&gt;, G'&lt;sub&gt;F&lt;/sub&gt;, G'&lt;sub&gt;D&lt;/sub&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="play"></category><category term="puzzles"></category><category term="rubik"></category></entry></feed>