<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Abby Nduta</title>
    <description>The latest articles on Forem by Abby Nduta (@ndutared).</description>
    <link>https://forem.com/ndutared</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F568384%2F33cb8bca-b935-47ab-ade4-dedb430444e5.png</url>
      <title>Forem: Abby Nduta</title>
      <link>https://forem.com/ndutared</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ndutared"/>
    <language>en</language>
    <item>
      <title>Learning with AI: An experiment</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Thu, 08 May 2025 08:26:04 +0000</pubDate>
      <link>https://forem.com/ndutared/learning-with-ai-an-experiment-1ebh</link>
      <guid>https://forem.com/ndutared/learning-with-ai-an-experiment-1ebh</guid>
      <description>&lt;h2&gt;
  
  
  Piping Challenge
&lt;/h2&gt;

&lt;p&gt;I wanted to try how well AI can explain a long chain of piping commands.&lt;/p&gt;

&lt;p&gt;The command:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explain the pipeline: history | cut -c 8- | sort | uniq -c | sort -nr | head&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I used ClaudeAI. I told it to explain the &lt;em&gt;pipe&lt;/em&gt; command in Linux simply and then explain each command, explaining the input and output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to understand the pipe command in Linux. I want you to simply explain piping and then explain each of these commands and then walk me step by step on what the outputs and inputs will be for each step: Explain the pipeline: history | cut -c 8- | sort | uniq -c | sort -nr | head&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I hadn’t understood what &lt;em&gt;unique commands&lt;/em&gt; were, so the extra explanation made it clear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy82ob5d7qwoguqfg7xjl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy82ob5d7qwoguqfg7xjl.png" alt="Image description" width="800" height="991"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was still confused about the 8 characters in the &lt;em&gt;cut -c 8-&lt;/em&gt; command so I asked Claude to explain this to me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6kh8qo0f9790ylncy3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6kh8qo0f9790ylncy3a.png" alt="Image description" width="800" height="1115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This didn’t make sense as in the initial explanation ClaudeAI had shown me only single digit history numbers in its output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpubjewj44k4uleoireyu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpubjewj44k4uleoireyu.png" alt="Image description" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I asked ClaudeAI about this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuy3tjx75tpm7baqwrnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuy3tjx75tpm7baqwrnp.png" alt="Image description" width="800" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I called it out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa676r5it27p3oku74tkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa676r5it27p3oku74tkg.png" alt="Image description" width="800" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I tested the history command on my terminal.&lt;/p&gt;

&lt;p&gt;And then I showed my input to ClaudeAI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpl6hc2fe02njln2d1wak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpl6hc2fe02njln2d1wak.png" alt="Image description" width="800" height="632"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI might not be the greatest starting point when learning new or complex concepts. You are better off learning from other sources and then confirming your understanding with AI playing the role of a student you are teaching what you have learned. &lt;/p&gt;

&lt;p&gt;It’s also important to test out something in your own environment. Like I tested the &lt;em&gt;history&lt;/em&gt; command on my terminal. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>generativeai</category>
    </item>
    <item>
      <title>How Customizable Agentic Systems Are Revolutionizing Business Operations</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Wed, 07 May 2025 15:11:36 +0000</pubDate>
      <link>https://forem.com/ndutared/how-customizable-agentic-systems-are-revolutionizing-business-operations-406g</link>
      <guid>https://forem.com/ndutared/how-customizable-agentic-systems-are-revolutionizing-business-operations-406g</guid>
      <description>&lt;p&gt;Artificial intelligence (AI) has become ubiquitous.&lt;/p&gt;

&lt;p&gt;In the heart of Utah, &lt;a href="https://www.opsystem.io/" rel="noopener noreferrer"&gt;OP Media Inc&lt;/a&gt;. has completed successful pilots, using AI to redefine information and process flows in &lt;a href="https://www.techbuzznews.com/op-media-transforms-operations-with-ai-enhanced-no-code-platforms-across-healthcare-emergency-planning-and-more/" rel="noopener noreferrer"&gt;healthcare and emergency services&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;OP Media uses an AI, dubbed AiBL core (pronounced Abel), to achieve these incredible feats. AiBL works by taking advantage of human knowledge within your business or organization (subject matter experts, or SMEs).&lt;/p&gt;

&lt;p&gt;AiBL core is a wonderful example of what has come to be known as agentic AI. Agentic AI systems “learn,” “grow,” and eventually make decisions with little human input.&lt;/p&gt;

&lt;p&gt;Your staff and team members contain a wealth of knowledge that could easily get lost if not well documented and shared.&lt;/p&gt;

&lt;p&gt;OP Media helps you create ops (not apps)—operational procedures that allow your team to document processes in real time and share them simply via URLs or QR codes.&lt;/p&gt;

&lt;p&gt;You can deploy AiBL core in your organization, and it’ll learn both from existing knowledge and SMEs.&lt;/p&gt;

&lt;p&gt;In this article, we explore agentic AI and compare it with its more popular counterpart, generative AI. We then look at how customizable agentic AIs, like AiBL, are transforming business processes. &lt;/p&gt;

&lt;p&gt;We also showcase AiBL customers who are successfully using OP Media’s solutions. Ultimately, we delve into the competitive advantage of organizations using customized AI agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agentic AI versus Generative AI
&lt;/h2&gt;

&lt;p&gt;All the jargon around AI can be perplexing. As a business leader, all you need are solutions that can help you achieve your goals. You don’t need to be bogged down with all the geekspeak.&lt;/p&gt;

&lt;p&gt;However, understanding different AI aspects can help you narrow down the solutions that are best suited for your business.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqb8f4e70zypc3e5h2ksc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqb8f4e70zypc3e5h2ksc.png" alt="Image description" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Generative AI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.oracle.com/ke/artificial-intelligence/generative-ai/what-is-generative-ai/" rel="noopener noreferrer"&gt;Generative AI&lt;/a&gt; (GenAI) is able to create text, data visualizations, and images based on “seeing” lots of similar data. &lt;/p&gt;

&lt;p&gt;The technology that powers GenAI is known as large language models (LLMs). These models are able to analyze large volumes of data, usually from the internet, and identify patterns, which they then use to create new text and visualizations.&lt;/p&gt;

&lt;p&gt;Some popular GenAI products built on LLMs include ChatGPT, Gemini, Claude, Grok, and Llama.&lt;/p&gt;

&lt;h3&gt;
  
  
  GenAI use cases
&lt;/h3&gt;

&lt;p&gt;GenAI solves &lt;a href="https://www2.deloitte.com/us/en/insights/industry/technology/technology-media-and-telecom-predictions/2025/autonomous-generative-ai-agents-still-under-development.html" rel="noopener noreferrer"&gt;real problems&lt;/a&gt; across industries. Volkswagen, for example, has created a GenAI system that allows drivers in the United States to study vehicle owner’s manuals and ask questions to better understand their owners’ cars.&lt;/p&gt;

&lt;p&gt;Another company creates “smarter” billboards. These billboards display localized ads that update in real time based on available data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agentic AI
&lt;/h3&gt;

&lt;p&gt;Agentic AI, on the other hand, is able to make decisions on behalf of a human. It has agency, like the name suggests. That might sound scary, but it only means that the AI takes steps based on the data it analyzes and the patterns it identifies.&lt;/p&gt;

&lt;h4&gt;
  
  
  AI agents
&lt;/h4&gt;

&lt;p&gt;Agentic AI takes advantage of AI agents. An AI agent is a specialized software system. For example, a customer service AI agent knows how to respond to customer questions because they have studied copious amounts of customer queries and responses.&lt;/p&gt;

&lt;p&gt;AI agents are able to learn from contexts. For instance, they can learn from their interactions with human customer service representatives. They can learn how humans respond and incorporate that into their “knowledge repertoire.”&lt;/p&gt;

&lt;h4&gt;
  
  
  LLMs, agentic AI and GenAI
&lt;/h4&gt;

&lt;p&gt;Agentic AI is similar to GenAI, as they are powered by the same technology, LLMs. However, a human must prompt Gen AI to determine its next course of action. Agentic AI is able to make a decision based on what it already knows. &lt;/p&gt;

&lt;p&gt;For example, if an AI agent is unable to resolve a customer complaint, it can refer the customer to the appropriate person, summarize previous interactions, and add relevant documents, making it easier to pick up from where the agent left.&lt;/p&gt;

&lt;p&gt;Even if generative AI is more popular when compared to agentic AI, the latter can be highly customizable, as it can learn even from humans. This capability makes it a splendid option for business solutions requiring some degree of autonomy.&lt;/p&gt;

&lt;h4&gt;
  
  
  Human on the loop
&lt;/h4&gt;

&lt;p&gt;It’s important to note that you can have a human review the decisions made by Agentic AI (known as a “&lt;a href="https://www2.deloitte.com/us/en/insights/industry/technology/technology-media-and-telecom-predictions/2025/autonomous-generative-ai-agents-still-under-development.html" rel="noopener noreferrer"&gt;human on the loop&lt;/a&gt;”). This way, any mistakes made can be noted and used to further streamline your AI agent’s data.&lt;/p&gt;

&lt;p&gt;When they become "stuck," AI agents could also turn to a human for "help." In this way, they can be considered junior employees who learn from seniors.&lt;/p&gt;

&lt;h4&gt;
  
  
  AI agents in tandem
&lt;/h4&gt;

&lt;p&gt;Another intriguing aspect of agentic AI is that multiple AI agents can work in tandem, further streamlining the “decision-making process.”&lt;/p&gt;

&lt;p&gt;Agentic AI is still in its early stages. There is still so much more it can accomplish in the near future.&lt;/p&gt;

&lt;h2&gt;
  
  
  How customizable agentic systems like AiBL are transforming operations
&lt;/h2&gt;

&lt;p&gt;Afu is OP Media’s CEO and founder. He has had a career spanning three decades in operations. &lt;/p&gt;

&lt;p&gt;Afu refers to the type of AI that AiBL provides as “authentic” intelligence. &lt;/p&gt;

&lt;h3&gt;
  
  
  Incorporating tribal knowledge
&lt;/h3&gt;

&lt;p&gt;AiBL is able to learn from front-line staff members. This characteristic is the main difference between AiBL and other agentic AI systems.&lt;/p&gt;

&lt;p&gt;AI systems do have “a human in the loop.” LLMs, for example, have human AI trainers to help them better phrase conversations.&lt;/p&gt;

&lt;p&gt;While AiBL can learn from organizational or business data, it is also able to learn from human SMEs. As these experts document processes, AiBL can pick up the new changes. &lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding principles rather than just steps
&lt;/h3&gt;

&lt;p&gt;AiBL can even question why the change was made, said Afu in a recent interview with Doug Jardine, Corporate Media and Communications Manager at OP Media. &lt;/p&gt;

&lt;p&gt;Think about a child who tries to understand by asking “why” until he/she understands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplifying knowledge capture and sharing
&lt;/h3&gt;

&lt;p&gt;OP Media customers interact with AiBL via the OP System, a dashboard that can help you create and document processes (tagged operating procedures, OPs) in your business or organization.&lt;/p&gt;

&lt;p&gt;Creating a process (OP) is relatively straightforward. OP Media also offers customer training to help you get on board faster.&lt;/p&gt;

&lt;p&gt;You can then share your documented process via URL or a QR code that is scannable even with a phone. &lt;/p&gt;

&lt;p&gt;The team receives real-time notifications when you introduce changes to your OP.&lt;/p&gt;

&lt;p&gt;Your processes and standard operating procedures don’t have to live on PDFs that are difficult to track, manage, and update.&lt;/p&gt;

&lt;p&gt;The OP System is a Software as a Service (SaaS) product accessible via the internet as long as you have an internet connection. You don’t need any coding knowledge to create an OP.&lt;/p&gt;

&lt;p&gt;It comes with features like access control, so you can choose who needs to be added to an OP. You can also manage user permissions to ensure that only the correct people edit the OP when needed. &lt;/p&gt;

&lt;p&gt;The OP system logs all data, which is helpful in understanding how your team is interacting with OPs. You can spot where staff is struggling and further streamline onboarding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meeting diverse security and compliance requirements
&lt;/h3&gt;

&lt;p&gt;You can deploy AiBL within your own business infrastructure as well, running it completely on-prem. This feature gives you full control of your data and helps you comply with regulations.&lt;/p&gt;

&lt;p&gt;OP Media has partnered with &lt;a href="https://virnetx.com/" rel="noopener noreferrer"&gt;VirnetX&lt;/a&gt;, giving you an extra layer of security. VirnetX is a strategic and leading security solutions provider helping you run your on-prem solutions with the utmost security.&lt;/p&gt;

&lt;p&gt;OP Media’s goal is to help companies, businesses, and organizations streamline their processes. You know your business and its processes. OP Media just provides the tech you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world applications featuring OP Media customer case studies
&lt;/h2&gt;

&lt;p&gt;The possibilities presented by agentic AI may seem akin to science fiction. It might be difficult to imagine any real-world applications.&lt;/p&gt;

&lt;p&gt;OP Media is leading the way in providing the technology and tools for some intriguing agentic AI use cases in healthcare, emergency services, and even national defense.&lt;/p&gt;

&lt;p&gt;There have been pilots in three Utah hospitals, Davis County’s emergency department, and with the Department of the Air Force Digital Transformation Office (DTO) via the &lt;a href="https://www.newswire.com/news/department-of-the-air-force-catalyst-accelerator-ogden-select-eight-21970686" rel="noopener noreferrer"&gt;Catalyst Operator program&lt;/a&gt; in Ogden, Utah.&lt;/p&gt;

&lt;h3&gt;
  
  
  Milford Memorial Hospital, Utah
&lt;/h3&gt;

&lt;p&gt;Milford Memorial Hospital, Utah, went through OP Media’s pilot process and is now an OP Media customer. OP Media helped them learn how to create OPs via weekly training.&lt;/p&gt;

&lt;p&gt;Guy Dansie, the hospital administrator, says that the OP system has helped them document institutional knowledge from staff members that could easily be lost. With the OP system, someone else can easily find a documented process if the person who usually works on a specific task is absent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“As you do it more, you start thinking about it, sometimes at night or sometimes when you are having a conversation with a coworker. You think, ‘my goodness!’ I need to have this process recorded somehow.&lt;/p&gt;

&lt;p&gt;So we have this great fear of losing that knowledge, institutional knowledge. &lt;/p&gt;

&lt;p&gt;So what the OP allows us to do is to maintain that legacy information and or if somebody comes along in an emergency or somebody's out or sick or whatever, then they can step in and open the OP and figure out how to do that job.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Guy adds that they share knowledge, expertise, and personnel, giving an example of a seasoned nurse who created an OP for the hospital’s emergency preparedness program.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We have to have an economy of scale with certain positions. So we do share knowledge, we share expertise, and we share personnel. So some of that, for example, our emergency management for the hospital.&lt;/p&gt;

&lt;p&gt;We had a wonderful nurse, a very seasoned nurse who had an excellent understanding of how to develop the emergency preparedness program for the hospital in Beaver, Utah, and we were able to implement that using her OP.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He concludes by highlighting that they discovered that there could even be more applications beyond their initial ideas. There is a learning curve, though, for the staff members to understand the OPs and use them effectively.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I think we're only limited by our imagination. I think as we started, I realized there are more and more applications. There are more things that I didn't think of initially. The way to grow our OPs use is actually to have our staff understand what the OPs are. And that takes time. It takes an educational learning curve.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Davis County Emergency Services
&lt;/h3&gt;

&lt;p&gt;OP Media also took Ember Herrick through their pilot process. Ember leads Davis County’s emergency services in Northern Utah. &lt;/p&gt;

&lt;p&gt;Davis County is now also an OP Media customer. Ember created an OP, which is essentially a &lt;a href="https://www.davisjournal.com/2024/09/05/504242/county-pilots-new-system-for-emergency-management" rel="noopener noreferrer"&gt;toolkit for new emergency managers&lt;/a&gt;. The OP was also used to map evacuation zones for residents, and it is accessible via a QR code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdgunnvz1ymjqwbo5xca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdgunnvz1ymjqwbo5xca.png" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the OP, new emergency managers can access all the information they need in one place, from a growing, up-to-date document. &lt;br&gt;
Emergency plans can also be updated on the fly, allowing for a free flow of information between residents, emergency managers, and the county. &lt;/p&gt;

&lt;p&gt;Ember is now spearheading the creation of live emergency plans for each city in Davis County. The information collected can also be useful beyond planning. It can serve as a foundation for emergency funding.&lt;/p&gt;

&lt;p&gt;She says that she liked that the pilot was “free” and that she’s always on the lookout for new technology to improve emergency preparedness in Davis County.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I am always looking for new technology to improve emergency preparedness in Davis County. Of course, I liked the sound of a ‘free’ pilot. I also liked the idea of being able to change a process while it's in progress because things are continually changing in a disaster.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ember also shares how using outdated documentation impacted reimbursement after a natural disaster and how difficult it was to update the documentation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“For example, after the 2020 windstorm that impacted Davis County public works staff began removing fallen trees from county property. Staff was documenting damages using preliminary damage assessment forms from 2011 when the last windstorm hit. &lt;/p&gt;

&lt;p&gt;Several weeks into the cleanup process FEMA notified us that we needed to use updated forms with some additional questions. It was a hassle to get the forms switched out to all staff and communicate to them why the change was happening, this led to incomplete documentation that impacted the amount of reimbursement the county was able to recover from FEMA.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ember added that, with OP media’s OPs, she could share a QR code in the wake of a disaster with a documented damage assessment process. She could also make updates and share them immediately with all necessary parties.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I was intrigued by the idea that I could share a QR code with public works in the immediate aftermath of a disaster with a predetermined damage assessment process in place and required fields in a survey. If additional questions needed to be added to the survey during the recovery I could make the update in OP Media and as soon as I published the changes, all public works staff would immediately have the latest version on their phone.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Department of the Air Force (DAF)
&lt;/h3&gt;

&lt;p&gt;In 2023, OP Media was one of the businesses selected to solve a &lt;a href="https://www.newswire.com/news/department-of-the-air-force-catalyst-accelerator-ogden-select-eight-21970686" rel="noopener noreferrer"&gt;DAF challenge&lt;/a&gt;, which involved streamlining certification processes.&lt;/p&gt;

&lt;p&gt;The DAF collaborates with the Catalyst Accelerator in Ogden to provide opportunities for technology companies to showcase and potentially provide national defense solutions.&lt;/p&gt;

&lt;p&gt;OP Media is currently working on a pilot with defense-based companies that resulted from this opportunity. &lt;/p&gt;

&lt;p&gt;As you can see, the possibilities are endless. As long as there is a process, OP Media can come in to help streamline and optimize it. It doesn’t matter whether that’s construction, defense, or healthcare.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices for leveraging knowledge from subject matter experts
&lt;/h2&gt;

&lt;p&gt;Ember and Guy are excellent examples of internal subject matter experts (SMEs) within an organization. SMEs are invaluable as they have institutional knowledge gained over time working for the company. They are good at their jobs and understand what works best for the business or organization.&lt;/p&gt;

&lt;p&gt;A crucial part of optimizing processes in an organization is having the right people. SMEs can come in with their expertise to help you create and optimize processes, which you can then document via an evolving OP.&lt;/p&gt;

&lt;p&gt;How do you identify potential SMEs in your company? How do you approach them to help with the goal you intend to achieve?&lt;/p&gt;

&lt;p&gt;Remember they already have other work priorities. Let’s find out how you can identify and work with SMEs in your business.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define the goal you want to achieve
&lt;/h3&gt;

&lt;p&gt;Defining what you want to achieve helps you find the right SMEs to work with. Your goal might be to document a process so that it’s easier for other people to follow, for example.&lt;/p&gt;

&lt;p&gt;Once you have the goal, it’s time to find the right people to help you achieve it. &lt;/p&gt;

&lt;h3&gt;
  
  
  Identify potential SMEs
&lt;/h3&gt;

&lt;p&gt;SMEs can be anyone in the organization, really. It could be people in management who have been with the company for a significant period. They could be people who have joined the company recently but have a lot of expertise in their field.&lt;/p&gt;

&lt;p&gt;A positive indicator of potential SMEs is employees who have an interest in personal growth. They, for example, attend conferences, write in publications (their own or otherwise), post on social media channels, are members of professional communities related to the industry, lead communities, or even sit on boards.&lt;/p&gt;

&lt;p&gt;Another great approach to finding SMEs is people who like to help others. They are the “go-to” person when other people in the team need help. They already have outstanding people skills and can explain things in a way that others can understand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build an SME database
&lt;/h3&gt;

&lt;p&gt;Once you have identified your potential SMEs, build a small database with their details. It could be a simple document with their names and specializations. Take some time to learn more about them. For instance, you could inquire about their hobbies and interests outside of work.&lt;/p&gt;

&lt;p&gt;The database should be a growing document that you can update easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach the SMEs
&lt;/h3&gt;

&lt;p&gt;Start talking to them and “pitching” what you want to achieve. Stay open-minded, as they may not have time to take on more responsibilities.&lt;/p&gt;

&lt;p&gt;Give them the option to contribute in ways they deem most fit. Some might end up only being involved in decision-making. Others might be able to do more “hands-on” work, like actually creating the processes or training other staff members on how to use the OP once created.&lt;/p&gt;

&lt;p&gt;You also need to consider the formats that they prefer to work with. Some might prefer to create videos or podcasts, while others may prefer to write.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get creating
&lt;/h3&gt;

&lt;p&gt;Once you have a small number of people, you can start to create the processes and OPs. The beautiful thing about OPs is they are customizable, so you can change or update them as needed. Give people the flexibility to join you and drop off if required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Onboarding
&lt;/h3&gt;

&lt;p&gt;Once the OPs are ready, you can teach people how to use them. It doesn’t have to be a whole conference. It could be 30 minutes before or after work, depending on their availability. &lt;/p&gt;

&lt;p&gt;The idea is to make the onboarding smooth, without causing major disruptions to day-to-day business activities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring
&lt;/h3&gt;

&lt;p&gt;Allow some time for individuals to engage with the OPs. Check the generated system data to see areas of struggle. Note any questions they may have.&lt;/p&gt;

&lt;p&gt;Periodically improve the OP. Eventually, you’ll have clearly documented, up-to-date processes that anyone can access.&lt;/p&gt;

&lt;p&gt;Remember, the OPs are shareable even as QR codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The competitive advantage of organizations that deploy customized AI agents versus those using generic AI tools
&lt;/h2&gt;

&lt;p&gt;Specialized AI agents have the potential to grossly outperform generic AI tools. &lt;/p&gt;

&lt;p&gt;For example, junior customer service representatives at a Fortune 500 software firm showed a &lt;a href="https://www.nber.org/system/files/working_papers/w31161/w31161.pdf" rel="noopener noreferrer"&gt;34% increase&lt;/a&gt; in the hourly customer query resolutions when working with an AI tool that monitors customer chats and suggests probable responses.&lt;/p&gt;

&lt;p&gt;Generic AI tools like ChatGPT, Claude, and Grok can be further tailored to meet your business and organizational needs. You can train them using your company’s data, for example, giving them context. &lt;/p&gt;

&lt;p&gt;AI agents, on the other hand, are highly customizable as well, learning from company data and even interactions with human subject matter experts.&lt;/p&gt;

&lt;p&gt;In this section, we explore the advantages of using customized AI, be it GenAI or agentic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better data privacy, control, and compliance
&lt;/h3&gt;

&lt;p&gt;Once you deploy an AI system within your business and train it using your company’s data, you start to have more control over your data’s privacy.&lt;/p&gt;

&lt;p&gt;Some data regulations require that your data stay within the physical confines of your business. If you serve customers in Europe, you also need to adhere to the General Data Protection Regulation (GDPR) and other policies around Personally Identifiable Information (PII).&lt;/p&gt;

&lt;p&gt;Bespoke AI solutions allow you to become and remain compliant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Faster strategic decision-making
&lt;/h3&gt;

&lt;p&gt;Agentic AI systems can communicate with other agentic systems within an AI framework, contributing to faster decision-making. &lt;/p&gt;

&lt;p&gt;For example, an AI agent whose primary job is to communicate with customers and help them with their complaints can “ask” for a specific customer’s account details from another AI agent that focuses on customer creditworthiness. It can then take the next appropriate action, based on the received data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improved operational efficiency and productivity
&lt;/h3&gt;

&lt;p&gt;Customized AI solutions can help you increase productivity and efficiency in your company. For example, OP Media’s AiBL could suggest updates after noticing a pattern. &lt;/p&gt;

&lt;p&gt;AiBL may notice that staff members keep on searching for a specific thing. They might be checking their company’s system for Health Insurance Portability and Accountability Act (HIPAA) compliance, for instance.&lt;/p&gt;

&lt;p&gt;AiBL can then suggest adding processes around HIPAA, as it is a popular or frequent search.&lt;/p&gt;

&lt;p&gt;Specialized AI systems have much more organizational knowledge. They “work” within your company or business. They can therefore provide information that’s more tailored to your business and customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;We have delved into the thrilling world of agentic AI. Both GenAI and agentic AI are built from the same foundational technology, LLMs. Agentic AI wins the tussle by virtue of being able to make decisions with little human input.&lt;/p&gt;

&lt;p&gt;OP Media is in the vanguard of agentic AI in business operations. Emergency services are just the tip of what AiBL can do.&lt;/p&gt;

&lt;p&gt;We hope that this article gives you various perspectives on why agentic AI might be the missing piece in your business operations. When you have made the decision, we hope you choose OP Media to walk with you in your process optimization journey.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>enterprisetech</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How To Set Up a VM With VirtualBox</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Sat, 29 Mar 2025 22:22:51 +0000</pubDate>
      <link>https://forem.com/ndutared/creating-a-vm-step-by-step-25fh</link>
      <guid>https://forem.com/ndutared/creating-a-vm-step-by-step-25fh</guid>
      <description>&lt;p&gt;The first time I had to set up a VM, I was preparing for my first ever OpenStack Horizon contribution. After trying it a couple of times of trying and failing, I gave up and did a local installation.&lt;/p&gt;

&lt;p&gt;I contributed fine, but OpenStack is quite heavy. I crashed my laptop and had to reinstall it. By the time I was done, I was a “local installation” expert” (check out some &lt;a href="https://dev.to/ndutared/debugging-devstack-9pe"&gt;devstack debugging tips&lt;/a&gt;, btw)&lt;/p&gt;

&lt;p&gt;I then decided to try and install it in a VM, and when I finally had it working it literally felt like I had a “small computer” inside my desktop. &lt;/p&gt;

&lt;p&gt;Jokes aside, I genuinely think that installing a VM successfully should be somewhere on a techie’s hall of fame, or a huge step to "growing up in tech."&lt;/p&gt;

&lt;p&gt;I hope this guide saves you some considerable time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You have installed &lt;a href="https://www.oracle.com/ke/virtualization/technologies/vm/downloads/virtualbox-downloads.html" rel="noopener noreferrer"&gt;VirtualBox &lt;/a&gt; and &lt;a href="https://www.oracle.com/cis/virtualization/technologies/vm/downloads/virtualbox-downloads.html" rel="noopener noreferrer"&gt;VirtualBox Extension Pack&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You have an Ubuntu ISO image downloaded (I used ubuntu-24.04.2-desktop-amd64.iso).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a new VM in VirtualBox
&lt;/h2&gt;

&lt;p&gt;We are now ready to start creating our VM.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open VirtualBox and click "New."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpg5fbksm41cre9juh9cm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpg5fbksm41cre9juh9cm.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name your VM.&lt;/li&gt;
&lt;li&gt;Choose the folder for the VirtualBox directory location (which is where you installed VirtualBox).&lt;/li&gt;
&lt;li&gt;Choose the previously downloaded Ubuntu desktop ISO image.&lt;/li&gt;
&lt;li&gt;Select "Linux" as the type, “Ubuntu” as the subtype, and "Ubuntu (64-bit)" as the version.&lt;/li&gt;
&lt;li&gt;Allocate at least 8GB of RAM (8192MB). I have 16 GB of RAM; this is me living dangerously.&lt;/li&gt;
&lt;li&gt;Create a new virtual hard disk (VDI) with at least 40GB of space (preferably 60 GB+). &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  A note on unattended install
&lt;/h4&gt;

&lt;p&gt;This part helps with automatic OS installations. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dfrd7mi4vi05rtqb42l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dfrd7mi4vi05rtqb42l.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add your username and password (leave the password as the default one as changing it causes errors)&lt;/li&gt;
&lt;li&gt;Hostname (I used DevStack-VM)&lt;/li&gt;
&lt;li&gt;Domain name (you can use the default)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  A word on the "install in background" option
&lt;/h4&gt;

&lt;p&gt;If you check the "install in background" box, you won’t be able to see the installation process, as it’ll be running in the background. I’ll not check it for now, so that if there are any issues, you can see them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnr2hwv9biexf61tdjnc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnr2hwv9biexf61tdjnc.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Another note: Guest additions
&lt;/h4&gt;

&lt;p&gt;The “Guest additions” checkbox allows further configuration between your host and VM. We’ll leave it unchecked for now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hardware configurations
&lt;/h3&gt;

&lt;p&gt;Base Memory: 8192 MB (8 GB)—perfect for DevStack requirements&lt;br&gt;
Processors: 4 CPUs&lt;/p&gt;
&lt;h3&gt;
  
  
  Hard Disk Configurations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We’ll create a new virtual hard disk with a size of 25 GB.&lt;/li&gt;
&lt;li&gt;File type: VDI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55gr3zvj5lghgtou432v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55gr3zvj5lghgtou432v.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can now click “finish” to create the VM.&lt;/li&gt;
&lt;li&gt;Wait for a few minutes for the Ubuntu installation and setup and other configurations to complete.&lt;/li&gt;
&lt;li&gt;You can power the VM off.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Successful installation
&lt;/h3&gt;

&lt;p&gt;If everything works well, you should be greeted with a “normal” Ubuntu login.&lt;/p&gt;

&lt;p&gt;Input your password, and voila!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdskhby5wk5a05r8nd6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdskhby5wk5a05r8nd6s.png" alt="Image description" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Set up the host network
&lt;/h3&gt;

&lt;p&gt;We need to shut down the VM again to create a host-only network. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To safely shut down the VM:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;shutdown&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Once the VM is shut down, we need to create a host-only network.&lt;/li&gt;
&lt;li&gt;Open VirtualBox and go to File &amp;gt; Tools &amp;gt; Network Manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpbvfvq2iixaaew0ohxnj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpbvfvq2iixaaew0ohxnj.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "Create." &lt;/li&gt;
&lt;li&gt;The network name should be "&lt;em&gt;vboxnet0&lt;/em&gt;."&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Configure the network adapters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Select your VM and click on "Settings."&lt;/li&gt;
&lt;li&gt;Go to the "Network" tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhjoguk62dm89g5uofe1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyhjoguk62dm89g5uofe1.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under "Adapter 1," check "Enable Network Adapter."&lt;/li&gt;
&lt;li&gt;Choose &lt;em&gt;Attached to NAT&lt;/em&gt; (this gives your VM internet access).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxxah1nt8fgljnxkjr6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxxah1nt8fgljnxkjr6q.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under "Adapter 2," check "Enable Network Adapter."&lt;/li&gt;
&lt;li&gt;Choose &lt;em&gt;Set to Host-Only Adapter&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Select the network you just created from the dropdown menu.&lt;/li&gt;
&lt;li&gt;Click OK to save the settings.&lt;/li&gt;
&lt;li&gt;Start your VM again.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Find the VM’s IP address on the host-only network
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;ip addr&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should show you your network interfaces and IP addresses. Check that you have IP addresses under &lt;em&gt;enp0s3&lt;/em&gt; and &lt;em&gt;enp0s8&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You’ll need the host-only network interface IP (enp0s8), as you’ll add it to your &lt;em&gt;local.conf&lt;/em&gt; file when setting up DevStack.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configure a static IP
&lt;/h3&gt;

&lt;p&gt;DevStack will use a static IP to give you access to the Horizon dashboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="mf"&gt;192.168&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;56.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="n"&gt;enp0s8&lt;/span&gt; 
&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="n"&gt;enp0s8&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After doing this, run &lt;code&gt;ip addr&lt;/code&gt; again to verify that enp0s8 has an IP address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forging Forward
&lt;/h2&gt;

&lt;p&gt;You are now one step closer to your DevStack installation. I will discuss this in a separate post. I hope this works for you and you have a smooth install time.&lt;/p&gt;

&lt;p&gt;Happy VMing!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://medium.com/@ollste/openstack-local-development-how-to-install-test-and-use-openstack-in-virtualbox-b60b667886c4" rel="noopener noreferrer"&gt;https://medium.com/@ollste/openstack-local-development-how-to-install-test-and-use-openstack-in-virtualbox-b60b667886c4&lt;/a&gt;&lt;/p&gt;

</description>
      <category>virtualmachine</category>
      <category>openstack</category>
      <category>virtualbox</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Creating a Patch on OpenStack: A Checklist</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Wed, 29 Jan 2025 09:28:48 +0000</pubDate>
      <link>https://forem.com/ndutared/creating-a-patch-on-openstack-2kd2</link>
      <guid>https://forem.com/ndutared/creating-a-patch-on-openstack-2kd2</guid>
      <description>&lt;p&gt;Creating a patch on OpenStack can be time-consuming. Often, you need to go back to documentation to make sure that you have not missed any steps.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.opendev.org/opendev/infra-manual/latest/developers.html#quick-reference" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; is, of course, well written. However, I find that there are a few things to keep in mind so that you can create a patch successfully at the first attempt.&lt;/p&gt;

&lt;p&gt;This article is more of a checklist for you when creating a patch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;p&gt;OpenStack uses &lt;a href="https://docs.openstack.org/contributors/en_GB/common/setup-gerrit.html" rel="noopener noreferrer"&gt;Gerrit&lt;/a&gt; as its review system and &lt;a href="https://tox.wiki/en/4.24.1/" rel="noopener noreferrer"&gt;tox&lt;/a&gt; as its virtual environment management and test command line tool.&lt;/p&gt;

&lt;p&gt;Let's go through the steps that you need to take to create a patch successfully the very first time.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Get the latest upstream changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;remote&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;checkout&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;pull&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;ff&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;origin&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create a topic branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;checkout&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;TOPIC&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;BRANCH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Make your code changes
&lt;/h3&gt;

&lt;p&gt;PyCharm helps you catch linting errors, so I'd advise using it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Run tests
&lt;/h3&gt;

&lt;p&gt;You can run tests in your tox virtual environment. You can also just run the test check in the directory or files that you made changes to.&lt;/p&gt;

&lt;p&gt;Running tests ensures that you can rectify basic issues, if any, even before reviewers get to your code, saving everyone's time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#run test checks within a tox virtual environment
&lt;/span&gt;&lt;span class="n"&gt;tox&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 

&lt;span class="c1"&gt;#run test checks within a particular directory
&lt;/span&gt;&lt;span class="n"&gt;tox&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;directory_path&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;#run test checks within a particular file
&lt;/span&gt;&lt;span class="n"&gt;tox&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Fix any errors highlighted
&lt;/h3&gt;

&lt;p&gt;When the tests are done, a summary is generated that shows whether they passed or failed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv9md0v0hgqykwsxuppm7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv9md0v0hgqykwsxuppm7.png" alt="Image description" width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The system also generates a file containing the test results. If there are any errors, you need to fix them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr951fz5ox8g6jg5ujg5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr951fz5ox8g6jg5ujg5p.png" alt="Image description" width="800" height="87"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OpenStack's extensive code base may contain warnings unrelated to the recent changes you made. Don't worry too much about warnings. If they are within the files you changed, you might want to fix them.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Check for any JavaScript linting and code style errors
&lt;/h3&gt;

&lt;p&gt;If you have any JavaScript code, you can check for any linting or code style errors. You can check for linting errors within a tox virtual environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tox&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;npm&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="n"&gt;lint&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Ensure you fix any errors highlighted.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg3tqact2udgrr9dq8d2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg3tqact2udgrr9dq8d2.png" alt="Image description" width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Check for any Python code style errors
&lt;/h3&gt;

&lt;p&gt;OpenStack has a lot of Python code. If you have made changes to JavaScript code, it is likely to interact with the Python code. You can check for style errors in a tox virtual environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tox&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;pep8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have any errors, fix them before proceeding.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkgl2nxrdgpcdnktbmnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkgl2nxrdgpcdnktbmnw.png" alt="Image description" width="800" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Craft a commit message
&lt;/h3&gt;

&lt;p&gt;You need to craft your commit message before you push your changes to Gerrit.To do that, you can use a text editor like vim or nano to ensure you meet the required &lt;a href="https://wiki.openstack.org/wiki/GitCommitMessages#Information_in_commit_messages" rel="noopener noreferrer"&gt;guidelines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3bn7yuvjoeaz6akrg0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3bn7yuvjoeaz6akrg0d.png" alt="Image description" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you create your patch, it automatically generates the Change-Id. You don't need to create one.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Commit your changes
&lt;/h3&gt;

&lt;p&gt;You need to commit your change before submitting it for a review.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;commit message&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  11. Submit your change for review
&lt;/h3&gt;

&lt;p&gt;You are now finally ready to submit your change for review by a project maintainer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;review&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a success message to show that your change was submitted successfully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4w61fvvgw2ge2n3o6sb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4w61fvvgw2ge2n3o6sb.png" alt="Image description" width="800" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  12. Wait for merging
&lt;/h3&gt;

&lt;p&gt;If your code changes are correct, then they should be merged in no time. If there are any more changes, you should get feedback from the maintainers.&lt;/p&gt;

&lt;h3&gt;
  
  
  13. Updating a change
&lt;/h3&gt;

&lt;p&gt;If you need to make any more changes, do so, and then repeat all these steps. This time, however, since you have already created a patch, you'll run a different command during your commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;amend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Forging Forward
&lt;/h2&gt;

&lt;p&gt;It takes a while to master these steps. Don't worry if you have to sit down and dedicate some time to focus on this.&lt;/p&gt;

&lt;p&gt;With practice, it could become second nature. For now, I hope this guide provides a checklist you can use to make your patch-creating process on OpenStack more seamless.&lt;/p&gt;

&lt;p&gt;PS: Feel free to subscribe to my newsletter on substack: &lt;a href="https://techiewhowrites.substack.com/" rel="noopener noreferrer"&gt;https://techiewhowrites.substack.com/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.opendev.org/opendev/infra-manual/latest/developers.html#quick-reference" rel="noopener noreferrer"&gt;https://docs.opendev.org/opendev/infra-manual/latest/developers.html#quick-reference&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>openstack</category>
      <category>openinfra</category>
      <category>cloud</category>
    </item>
    <item>
      <title>6 Lessons From Implementing a Feature on OpenStack Horizon</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Mon, 18 Nov 2024 09:39:59 +0000</pubDate>
      <link>https://forem.com/ndutared/6-lessons-from-implementing-a-feature-on-openstack-horizon-328b</link>
      <guid>https://forem.com/ndutared/6-lessons-from-implementing-a-feature-on-openstack-horizon-328b</guid>
      <description>&lt;p&gt;Implementing the deactivate/reactivate feature was probably the most daunting experience in my tech journey.&lt;/p&gt;

&lt;p&gt;I started contributing to &lt;a href="https://www.openstack.org/" rel="noopener noreferrer"&gt;OpenStack&lt;/a&gt; as part of the &lt;a href="https://www.outreachy.org/" rel="noopener noreferrer"&gt;Outreachy&lt;/a&gt; program. I knew from the outset that I would be implementing some features from scratch, and I was scared.&lt;/p&gt;

&lt;p&gt;Like any new contributor, I spent the first few weeks of my OpenStack Horizon contribution fixing bugs and writing tests.&lt;/p&gt;

&lt;p&gt;However, the moment arrived for me to plunge into the depths of the pool. Sink or swim.&lt;/p&gt;

&lt;p&gt;This post is a reflection about the decisions I made, the steps I took, and the lessons I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preliminary work
&lt;/h2&gt;

&lt;p&gt;When you choose a project to contribute to under the Outreachy program, you get to see the features you’ll be working on.&lt;/p&gt;

&lt;p&gt;I wasn’t sure which one I was going to start with, so my mentor suggested starting with the deactivate/reactivate feature.&lt;/p&gt;

&lt;p&gt;The feature allows a user to change an image's status from 'active' to 'deactivated'.&lt;/p&gt;

&lt;p&gt;Deactivating an image &lt;a href="https://wiki.openstack.org/wiki/Glance-deactivate-image" rel="noopener noreferrer"&gt;restricts further instance builds based on it.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffemjrhbix6g1doxddlp2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffemjrhbix6g1doxddlp2.png" alt="Image description" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding resources
&lt;/h3&gt;

&lt;p&gt;I prefer to narrow down things before I begin to work on them. My first step, therefore, was to find resources that talked about the feature I was going to implement.&lt;/p&gt;

&lt;p&gt;And the fastest way to do that was to ask my mentor whether she could send me some resources to get started.&lt;/p&gt;

&lt;p&gt;She graciously sent some to me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delving into resources
&lt;/h3&gt;

&lt;p&gt;I spent some time reading through the resources. The deactivate/reactivate feature already existed in Glance (OpenStack’s images service).&lt;/p&gt;

&lt;p&gt;All I needed to do was implement it in Horizon.&lt;/p&gt;

&lt;p&gt;The Glance docs had even specified the endpoints to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deciding on an approach
&lt;/h3&gt;

&lt;p&gt;I had previously interacted with the Openstack Horizon dashboard when expanding the &lt;a href="https://dev.to/ndutared/delving-into-angular-in-openstack-horizon-directives-28jj"&gt;features displayed in an image drawer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My initial approach involved incorporating the deactivate/reactivate feature into the "Edit Image" workflow.&lt;/p&gt;

&lt;p&gt;This implies that I would include it as an option when a user wished to edit an image on the Horizon dashboard.&lt;/p&gt;

&lt;p&gt;When you click the "Launch" dropdown on a specific image, one of the options is "Edit Image".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvfbw4ukles99x7gsl9i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhvfbw4ukles99x7gsl9i.jpg" alt="Image description" width="382" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you click "Edit Image," it opens a modal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkpgu5gy5jb15x2m8wuz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkpgu5gy5jb15x2m8wuz.png" alt="Image description" width="800" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first, I wanted to create a third tab after "Metadata," which would allow a user to deactivate an image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhz5b5nz89b6yl88s4zh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhz5b5nz89b6yl88s4zh.png" alt="Image description" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would then implement the deactivate/reactivate feature as a toggle button. At face value, all I needed was a controller and a view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons galore
&lt;/h2&gt;

&lt;p&gt;Having previously worked with &lt;a href="https://dev.to/ndutared/delving-into-angular-in-openstack-horizon-directives-28jj"&gt;controllers and views&lt;/a&gt;, I expected it to be a walk in the park.&lt;/p&gt;

&lt;p&gt;This is where the lessons truly began to rack up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 1 : Understanding the project structure saves a lot of time
&lt;/h3&gt;

&lt;p&gt;I followed the project structure, and since I wanted to include my feature in the edit workflow, it was obvious that I would add it as another "step."&lt;/p&gt;

&lt;p&gt;I studied the other "steps," how the controllers are written, how the different modules interacted with each other, and ensured that I could trace where every variable was defined or imported from.&lt;/p&gt;

&lt;p&gt;Eventually, I began to see the patterns, and what needed to be imported from other directories and modules.&lt;/p&gt;

&lt;p&gt;Of interest was the static directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&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;horizon&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;static&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which defined various templates and how to use them in your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 2: The business logic may not always be obvious
&lt;/h3&gt;

&lt;p&gt;After writing the front-end Angular code it was time to implement the back-end functionality, which meant making a call to the Glance API.&lt;/p&gt;

&lt;p&gt;This was a bit of a hassle. The Glance docs recommended using &lt;a href="https://wiki.openstack.org/wiki/Glance-deactivate-image" rel="noopener noreferrer"&gt;"POST" instead of "PATCH"&lt;/a&gt; as the HTTP method, and both Glance and Horizon had different URL paths. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3l8y53ewtarwr9snut3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3l8y53ewtarwr9snut3.png" alt="Image description" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After spending lots of time on this, it was time for the ultimate blocker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 3: Ask for help with blockers
&lt;/h3&gt;

&lt;p&gt;I had implemented the feature fully and explored every possibility I could think of, but the frontend was not communicating with the backend.&lt;/p&gt;

&lt;p&gt;I checked the payload on developer tools, and the update was happening on the frontend. Despite calling the API from the backend, no update occurred.&lt;/p&gt;

&lt;p&gt;Eventually, my mentor was able to figure out the missing part of the puzzle. We needed to add some more code in a different Django directory for the view to process the POST request made to the "deactivate endpoint."&lt;/p&gt;

&lt;p&gt;The feature was now ready to go. Time to merge? Well, not that fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 4: Becoming a good engineer means taking care of code structure, linting, and ensuring that your code passes tests
&lt;/h3&gt;

&lt;p&gt;Before pushing your code via Gerrit, you need to run some tests. OpenStack uses &lt;a href="https://tox.wiki/en/4.23.2/" rel="noopener noreferrer"&gt;tox&lt;/a&gt; as its standard testing tool.&lt;/p&gt;

&lt;p&gt;Once your tests have passed, you also need to check for lint errors. Thankfully, tox will help you identify any lint errors before you push your code.&lt;/p&gt;

&lt;p&gt;In the event that you push code with lint errors, &lt;a href="https://zuul-ci.org/" rel="noopener noreferrer"&gt;Zuul&lt;/a&gt; will highlight them too, making sure that only properly formatted and error free code is merged.&lt;/p&gt;

&lt;p&gt;A really cool thing I learned was that Horizon’s NPM tests use thresholds to estimate code’s security.&lt;/p&gt;

&lt;p&gt;This means that if a particular threshold is passed, the code is flagged. In my case, since I had added more features (which translated to more functions and variables), I had upset the threshold. The short-term solution was to modify the threshold.&lt;/p&gt;

&lt;p&gt;Ultimately, writing tests would solve the threshold problem.&lt;/p&gt;

&lt;p&gt;I love the fact that OpenStack has all these processes in place—linting, tox for testing, and Zuul for CI/CD. It would be a total mess to try and debug after code has already been merged. &lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 5: Lesson 5: PyCharm is arguably better than VS Code for Linting
&lt;/h3&gt;

&lt;p&gt;PyCharm has really great linting options and is arguably better at it than VS Code. That said, I understand that there are impressive VSCode extensions to help with linting.&lt;/p&gt;

&lt;p&gt;Despite its steeper learning curve, I found PyCharm to be significantly superior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 6: Be open to changing your approach
&lt;/h3&gt;

&lt;p&gt;At a certain point, despite correcting all linting errors, the tests continued to fail. It was time to attempt a different approach.&lt;/p&gt;

&lt;p&gt;Instead of including the deactivate/activate feature as part of the "Edit Workflow," we decided to implement it as a separate action.&lt;/p&gt;

&lt;p&gt;This meant that you could choose it from the dropdown next to "Launch."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frskvxc730c8k8hi8husi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frskvxc730c8k8hi8husi.png" alt="Image description" width="800" height="888"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second approach would also reduce redundancy. The initial approach created an additional call to the API.&lt;/p&gt;

&lt;p&gt;The new approach also included changing the commit message to reflect what I was implementing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forging forward
&lt;/h2&gt;

&lt;p&gt;The OpenStack Horizon project is huge and complex. There’s still a lot to learn.&lt;/p&gt;

&lt;p&gt;I hope these lessons will help you keep some things in mind as you contribute to OpenStack.&lt;/p&gt;

&lt;p&gt;On to the next adventure. Writing tests 🚀&lt;/p&gt;

</description>
      <category>openstack</category>
      <category>opensource</category>
      <category>outreachy</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>Debugging DevStack</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Sat, 28 Sep 2024 09:50:14 +0000</pubDate>
      <link>https://forem.com/ndutared/debugging-devstack-9pe</link>
      <guid>https://forem.com/ndutared/debugging-devstack-9pe</guid>
      <description>&lt;p&gt;I first installed OpenStack via DevStack months ago. I honestly forgot how frustrating it can be. &lt;/p&gt;

&lt;p&gt;I followed the &lt;a href="https://docs.openstack.org/devstack/latest/" rel="noopener noreferrer"&gt;official docs&lt;/a&gt; and an &lt;a href="https://www.digitalocean.com/community/tutorials/install-openstack-ubuntu-devstack#step-6-accessing-openstack-on-a-web-browser" rel="noopener noreferrer"&gt;article&lt;/a&gt; from DigitalOcean.&lt;/p&gt;

&lt;p&gt;I'll summarize the main steps so that we can focus on debugging. Note that you need a fresh Ubuntu install for DevStack to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Install Linux&lt;/li&gt;
&lt;li&gt;Add Stack User&lt;/li&gt;
&lt;li&gt;Download DevStack&lt;/li&gt;
&lt;li&gt;Create a local.conf file&lt;/li&gt;
&lt;li&gt;Start the install&lt;/li&gt;
&lt;li&gt;Access OpenStack on a web browser&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usually, these steps should work seamlessly. &lt;/p&gt;

&lt;p&gt;However, with subsequent installs, things may not go as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging options
&lt;/h2&gt;

&lt;p&gt;There are some things you could try first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Doing a fresh Ubuntu install and then repeating the above mentioned main steps&lt;/li&gt;
&lt;li&gt;Running a most recent stable branch of devstack, not master. &lt;/li&gt;
&lt;li&gt;Running './unstack.sh' and then './clean.sh'&lt;/li&gt;
&lt;li&gt;Cleaning your 'opt/stack' directory&lt;/li&gt;
&lt;li&gt;Running './stack.sh' again using the stable branch of devstack&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Checking your errors
&lt;/h2&gt;

&lt;p&gt;At this point in time, you have tried all the recommended debugging options and are convinced that there is a devstack bug somewhere. &lt;/p&gt;

&lt;p&gt;There are a few workarounds depending on the errors you get, as well as a few changes to the local.conf file.&lt;/p&gt;

&lt;h3&gt;
  
  
  My errors
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6qysmdvmxn2rxt8we0o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6qysmdvmxn2rxt8we0o.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, I was getting a '&lt;a href="mailto:devstack@etcd.service"&gt;devstack@etcd.service&lt;/a&gt;' error. &lt;a href="https://stackoverflow.com/questions/50045173/installing-devtsack-using-stack-sh-fails-at-devstacketcd-service" rel="noopener noreferrer"&gt;Stack overflow&lt;/a&gt; gave me the first workaround. I added 'disable_service etcd3' to my local.conf file and run 'stack.sh' again.&lt;/p&gt;

&lt;p&gt;This cleared the '&lt;a href="mailto:devstack@etcd.service"&gt;devstack@etcd.service&lt;/a&gt;' error but introduced a 'keystone did not start' &lt;a href="https://bugs.launchpad.net/devstack/+bug/1697113?comments=all" rel="noopener noreferrer"&gt;error&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After spending hours and hours down &lt;a href="https://stackoverflow.com/questions/15596370/openstack-keystone-failing-to-start" rel="noopener noreferrer"&gt;rabbit holes&lt;/a&gt;, after a brainstorming session with a friend, we suspected that the IP address could be the problem.&lt;/p&gt;

&lt;p&gt;I had set my HOST_IP as my computer's IP address. I was reinstalling DevStack using a different network and there was a possibility that it could be picking the wrong IP address. &lt;/p&gt;

&lt;p&gt;Initially, I tried to include my ISP's router IP address, but it didn't help.&lt;/p&gt;

&lt;h3&gt;
  
  
  Home run
&lt;/h3&gt;

&lt;p&gt;I eventually changed my HOST_IP to 127.0.0.1 and passed it to the SERVICE_HOST.&lt;/p&gt;

&lt;p&gt;I also added enabled Horizon in my local.conf 'ENABLED_SERVICES+=,horizon'&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnxkvfabdp2hmhhldy68e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnxkvfabdp2hmhhldy68e.png" alt="Image description" width="800" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then rerun 'stack.sh' and everything worked!!!!&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Debugging DevStack can be challenging. What are the bugs you've encountered? Feel free to share in the comments.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>openstack</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Delving into Angular in OpenStack Horizon: Directives</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Wed, 07 Aug 2024 07:11:53 +0000</pubDate>
      <link>https://forem.com/ndutared/delving-into-angular-in-openstack-horizon-directives-28jj</link>
      <guid>https://forem.com/ndutared/delving-into-angular-in-openstack-horizon-directives-28jj</guid>
      <description>&lt;p&gt;I've been exploring &lt;a href="https://review.opendev.org/c/openstack/horizon/+/925355" rel="noopener noreferrer"&gt;Angular within the context of OpenStack Horizon&lt;/a&gt;. First, it's important to know that the Horizon project uses Angular1 (AngularJS) which is quite old and deprecated. It's an old code base. &lt;/p&gt;

&lt;p&gt;Your best bet at learning Angular1 is &lt;a href="https://docs.angularjs.org/guide" rel="noopener noreferrer"&gt;docs&lt;/a&gt; or this very useful &lt;a href="https://www.amazon.com/AngularJS-Brad-Green/dp/1449344852" rel="noopener noreferrer"&gt;book&lt;/a&gt; written by Brad Green and Shyam Seshadri.&lt;/p&gt;

&lt;p&gt;Lastly, the code base uses &lt;a href="https://www.w3schools.com/js/js_es5.asp" rel="noopener noreferrer"&gt;ES5&lt;/a&gt;. Keeping this in mind will come in handy as it may be difficult to find resources or learning material. Most resources will teach you up-to-date modern Angular (Angular 2 and all subsequent versions).&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminologies
&lt;/h2&gt;

&lt;p&gt;It's advisable to learn AngularJS before tinkering with Horizon. I'll define the main concepts you need to know, though this blog focuses on the HTML, where you find directives.&lt;/p&gt;

&lt;p&gt;AngularJS is based on the &lt;a href="https://www.tutorialspoint.com/angularjs/angularjs_mvc_architecture.htm" rel="noopener noreferrer"&gt;MVC&lt;/a&gt; concept, Model-View-Controller.&lt;/p&gt;

&lt;p&gt;This means that you separate your code into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;models&lt;/strong&gt; - the data to use in your project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;views&lt;/strong&gt; - the HTML that the user/&lt;a href="https://www.w3schools.com/js/js_htmldom.asp" rel="noopener noreferrer"&gt;DOM&lt;/a&gt; interacts with&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;controllers&lt;/strong&gt; - where you keep your code's logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Views
&lt;/h3&gt;

&lt;p&gt;Views in Horizon are HTML files. They are made up of HTML elements and directives.&lt;/p&gt;

&lt;p&gt;Directives are some sort of tags that we use to make our views more dynamic. Both AngularJS and Horizon have built-in directives, though you can create custom ones too. We'll learn more about them in our example. &lt;/p&gt;

&lt;p&gt;Horizon also uses UI Bootstrap directives, though we won't be encountering them in this article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Models
&lt;/h3&gt;

&lt;p&gt;Horizon models are a bit different, since we make API calls to the OpenStack service we want, for example, Glance for images, rather than from a database. &lt;/p&gt;

&lt;p&gt;We, however, within the context of Angular use models to store properties, for example.&lt;/p&gt;

&lt;h3&gt;
  
  
  Controllers
&lt;/h3&gt;

&lt;p&gt;We make the API calls to the OpenStack service in the controller, return the results, and show them via the view.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modules
&lt;/h3&gt;

&lt;p&gt;Modules come in when we want to work with dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Services
&lt;/h3&gt;

&lt;p&gt;Services come in when we want to perform specific tasks. &lt;/p&gt;

&lt;p&gt;I've tried to keep the jargon simple, but you can learn more about the concepts from the &lt;a href="https://docs.angularjs.org/guide" rel="noopener noreferrer"&gt;AngularJS docs&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Directives in Horizon
&lt;/h2&gt;

&lt;p&gt;Like we mentioned, directives are 'special tags' we add to our views to make them dynamic.&lt;/p&gt;

&lt;p&gt;Both AngularJS and Horizon have their own directives. I'm going to be making changes to the Images table on the Horizon dashboard. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F489abitsjm6wx2lywqb3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F489abitsjm6wx2lywqb3.png" alt="Image description" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Specifically, to the additional information you see when you expand the chevron arrow (&amp;gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd58aawrp406l1pyihghw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd58aawrp406l1pyihghw.png" alt="Image description" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To begin, let's look at how the view looks like. Here's the &lt;em&gt;drawer.html&lt;/em&gt; file we'll be working with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/openstack/horizon/blob/master/openstack_dashboard/static/app/core/images/details/drawer.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;horizon.app.core.images.DrawerController as drawerCtrl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hz&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;
    &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OS::Glance::Image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[
      ['name', 'id'],
      ['visibility', 'protected'],
      ['disk_format', 'size'],
      ['min_disk', 'min_ram']]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/hz-resource-property-list&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drawerCtrl.metadataDefs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-sm-12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;
        &lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;::drawerCtrl.metadataDefs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item.properties || item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/metadata-display&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right off the bat, you can see some 'strange' tags like &lt;em&gt;ng-controller&lt;/em&gt;, &lt;em&gt;hz-resource-property-list&lt;/em&gt;, and &lt;em&gt;ng-if&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;These are what we call 'directives'. The 'ng' prefix means that they are built-in Angular directives. The 'hz' directives means that they are Horizon directives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ng-controller&lt;/strong&gt; specifies the controller we are going to use in the view. This should be the DrawerController aliased as drawerCtrl, and defined in the specified file path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;hz-resource-property-list&lt;/strong&gt; is used to display a set of properties, and shows them in key-value pairs. It uses property groups, which divides the properties into lists.&lt;/p&gt;

&lt;p&gt;These groups will be displayed as columns. We use any number of columns. &lt;/p&gt;

&lt;p&gt;The groups can have as many properties as you need them to have.&lt;/p&gt;

&lt;p&gt;In our code, inside the &lt;em&gt;hzResourcePropertyList&lt;/em&gt; (this is how we write directives, though we use lowercase with dashes in the view as in &lt;em&gt;hz-resource-property-list&lt;/em&gt;) we have 'property-groups' which shows that we'll display &lt;em&gt;name&lt;/em&gt;, &lt;em&gt;id&lt;/em&gt;, &lt;em&gt;visibility&lt;/em&gt;, &lt;em&gt;protected&lt;/em&gt;, &lt;em&gt;disk_format&lt;/em&gt;, &lt;em&gt;size&lt;/em&gt;, &lt;em&gt;min_disk&lt;/em&gt;, and &lt;em&gt;min_ram&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hz&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;
    &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OS::Glance::Image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[
      ['name', 'id'],
      ['visibility', 'protected'],
      ['disk_format', 'size'],
      ['min_disk', 'min_ram']]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/hz-resource-property-list&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;resource-type-name&lt;/strong&gt; also has some strange syntax. Horizon uses something called HEAT type names to specify the name of the resource we're working with, Glance in this case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hz&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;
    &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OS::Glance::Image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[
      ['name', 'id'],
      ['visibility', 'protected'],
      ['disk_format', 'size'],
      ['min_disk', 'min_ram']]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/hz-resource-property-list&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;item&lt;/strong&gt; is the data object that contains the properties that we want to display.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ng-if&lt;/strong&gt; is a built-in AngularJS directive that helps us show information conditionally, just like you would in an if-else statement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Files
&lt;/h3&gt;

&lt;p&gt;To understand the code in the &lt;em&gt;drawer.html&lt;/em&gt; file, you need to look at additional files. &lt;/p&gt;

&lt;p&gt;For example, &lt;em&gt;ng-controller&lt;/em&gt; points to a controller named "Drawer Controller". This is defined in&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/openstack/horizon/blob/master/openstack_dashboard/static/app/core/images/summary.controller.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * @ngdoc controller
   * @name horizon.app.core.images.DrawerController
   * @description
   * This is the controller for the images drawer (summary) view.
   * Its primary purpose is to provide the metadata definitions to
   * the template via the ctrl.metadataDefs member.
   */&lt;/span&gt;
  &lt;span class="nx"&gt;angular&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;horizon.app.core.images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;horizon.app.core.images.DrawerController&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$inject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;horizon.app.core.openstack-service-api.glance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;horizon.app.core.images.resourceType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;metadataDefs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;glance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;imageResourceType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadataDefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metadataDefs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadataDefs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;applyMetadataDefinitions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;applyMetadataDefinitions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;glance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNamespaces&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;resource_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;imageResourceType&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setMetadefs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadataDefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I won't delve into this code much.&lt;/p&gt;

&lt;p&gt;You might want to also look at the overview file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/openstack/horizon/blob/master/openstack_dashboard/static/app/core/images/details/overview.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It displays all the possible properties we can get from Glance.&lt;/p&gt;

&lt;p&gt;On the dashboard, you'll see all these properties when you click on the name of the image, in this case, 'ndutas_image'&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygbf1319c6wgpvk4dnie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygbf1319c6wgpvk4dnie.png" alt="Image description" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see all the properties.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjggqzi4n8sj9z292zqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjggqzi4n8sj9z292zqs.png" alt="Image description" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of importance is that the properties are grouped into three. This is reflected in the &lt;em&gt;overview.html&lt;/em&gt; code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ImageOverviewController as ctrl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-md-6 detail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hz&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;
        &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OS::Glance::Image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dl-horizontal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ctrl.image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[[
          'id', 'name', 'type', 'status', 'size', 'min_disk', 'min_ram', 'disk_format',
          'container_format', 'created_at', 'updated_at']]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/hz-resource-property-list&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-md-6 detail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Security&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dl&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dl-horizontal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Owner&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dt&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dd&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Filename&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dt&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;noValue&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dd&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Visibility&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dt&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="na"&gt;imageVisibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projectId&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dd&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Protected&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dt&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;protected&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;yesno&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dd&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Checksum&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dt&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checksum&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;noValue&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dd&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dl&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-md-6 detail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Custom&lt;/span&gt; &lt;span class="nx"&gt;Properties&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dl&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dl-horizontal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;repeat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prop in ctrl.image.properties&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tooltip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{$ prop.name $}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourceType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dt&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dd&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dl&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will become important when we start to make changes.&lt;/p&gt;

&lt;p&gt;If you want to see where this properties are listed, go to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/openstack/horizon/blob/master/openstack_dashboard/static/app/core/images/images.module.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the imageProperties function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;imageProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imagesService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statuses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;checksum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checksum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Members&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;min_disk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Min. Disk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;min_ram&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Min. RAM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Owner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tags&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;updated_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Updated At&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mediumDate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;virtual_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Virtual Size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Visibility&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;architecture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Architecture&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;kernel_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Kernel ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;ramdisk_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ramdisk ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;created_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Created At&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mediumDate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;container_format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Container Format&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uppercase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;disk_format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Disk Format&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;noValue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uppercase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;is_public&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Is Public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yesno&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;protected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Protected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yesno&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bytes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;gettext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;statuses&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Showing More Properties in the Images Drawer
&lt;/h2&gt;

&lt;p&gt;I want to add more properties to the images drawer, specifically, &lt;em&gt;checksum&lt;/em&gt;, &lt;em&gt;os_hash_value&lt;/em&gt; and &lt;em&gt;os_hash_algo&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Currently, these do not exist on the dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6r5a8zq23j0gr41m3gb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6r5a8zq23j0gr41m3gb.png" alt="Image description" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Checksum is listed under the main properties to be shown in images.module.js.&lt;/p&gt;

&lt;p&gt;This means that we can display it under the hzResourcePropertyList directive in &lt;em&gt;drawer.html&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To do this, we need to add it as a property group.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hz&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;
    &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OS::Glance::Image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[
      ['name', 'id'],
      ['visibility', 'protected'],
      ['disk_format', 'size'],
      ['min_disk', 'min_ram'],
      ['checksum']]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/hz-resource-property-list&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;os_hash_value&lt;/em&gt; and &lt;em&gt;os_hash_algo&lt;/em&gt; are however not listed. If we check &lt;em&gt;overview.html&lt;/em&gt;, there's custom properties code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-md-6 detail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt; &lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Custom&lt;/span&gt; &lt;span class="nx"&gt;Properties&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dl&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dl-horizontal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;repeat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prop in ctrl.image.properties&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tooltip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{$ prop.name $}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resourceType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dt&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dd&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dl&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;os_hash_value&lt;/em&gt; and &lt;em&gt;os_hash_algo&lt;/em&gt; are probably custom properties. How do we display them in the drawer?&lt;/p&gt;

&lt;p&gt;Given that custom properties are still shown when you click the name of the image, it means that they are returned when we make the API call from Glance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5n34jjzsp7pdibrnynvl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5n34jjzsp7pdibrnynvl.png" alt="Image description" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we don't want to show all the custom properties, we can conditionally show just &lt;em&gt;os_hash_algo&lt;/em&gt; and &lt;em&gt;os_hash_value&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;col-sm-12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dl&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item.properties.os_hash_algo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;os_hash_algo&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dt&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;os_hash_algo&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dd&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ng&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item.properties.os_hash_value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;os_hash_value&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dt&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;os_hash_value&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dd&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/dl&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tox&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="nx"&gt;runserver&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now see the changes. Note that you may need to restart your computer if you can't see any changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1edt30zq7a3bawykignb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1edt30zq7a3bawykignb.png" alt="Image description" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Forging Forward
&lt;/h2&gt;

&lt;p&gt;This is a very simple introduction to Angular in OpenStack Horizon. There's definitely lots more to learn!!! I hope this guides you to try out Angular within the OpenStack Horizon context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://superuser.openinfra.dev/articles/dive-deep-into-openstack-horizon/" rel="noopener noreferrer"&gt;https://superuser.openinfra.dev/articles/dive-deep-into-openstack-horizon/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.angularjs.org/guide" rel="noopener noreferrer"&gt;https://docs.angularjs.org/guide&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.amazon.com/AngularJS-Brad-Green/dp/1449344852" rel="noopener noreferrer"&gt;https://www.amazon.com/AngularJS-Brad-Green/dp/1449344852&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.w3schools.com/js/js_es5.asp" rel="noopener noreferrer"&gt;https://www.w3schools.com/js/js_es5.asp&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.tutorialspoint.com/angularjs/angularjs_mvc_architecture.htm" rel="noopener noreferrer"&gt;https://www.tutorialspoint.com/angularjs/angularjs_mvc_architecture.htm&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/openstack/horizon/" rel="noopener noreferrer"&gt;https://github.com/openstack/horizon/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openstack</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Exploring Unit Testing in OpenStack Horizon</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Wed, 10 Jul 2024 06:54:30 +0000</pubDate>
      <link>https://forem.com/ndutared/openstack-horizon-week-7-exploring-unit-testing-la1</link>
      <guid>https://forem.com/ndutared/openstack-horizon-week-7-exploring-unit-testing-la1</guid>
      <description>&lt;p&gt;Context: I was &lt;a href="https://dev.to/ndutared/my-outreachy-application-journey-28m1"&gt;accepted into &lt;em&gt;Outreachy&lt;/em&gt;&lt;/a&gt; a few weeks ago and I'm working on &lt;em&gt;OpenStack Horizon&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I've been working with &lt;em&gt;Cinder&lt;/em&gt;, which is Block Storage on &lt;em&gt;OpenStack&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I've been spending my time learning how to &lt;a href="https://review.opendev.org/c/openstack/horizon/+/922788" rel="noopener noreferrer"&gt;write unit tests&lt;/a&gt; on the OpenStack Horizon project.&lt;/p&gt;

&lt;p&gt;I hope to share my reflections and what I've learned about writing unit tests on OpenStack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Start With Unit Tests?
&lt;/h2&gt;

&lt;p&gt;Unit tests are a great way to understand how various parts of the code interact with each other, and it's a&lt;a href="https://www.youtube.com/watch?v=1IsJHWFGxlQ" rel="noopener noreferrer"&gt; highly recommended approach&lt;/a&gt; by the OpenStack community.&lt;/p&gt;

&lt;p&gt;The main idea around unit tests is testing the smallest "piece of code" possible, usually a function or method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing Unit Tests on OpenStack Horizon
&lt;/h2&gt;

&lt;p&gt;I would like to begin by sharing some pitfalls to avoid. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polish up on your knowledge&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I would suggest polishing up on your unit testing basics first before attempting to write any unit tests. I'll share resources later on.&lt;/p&gt;

&lt;p&gt;A lot of Cinder tests use mocking and patching. Learn about that too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go through existing tests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You also need to check how the rest of the tests are written. This will give you a clue about what's expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Follow the inheritance trail&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cinder&lt;/em&gt; code is basically Django classes, which means lots of inheritance. Make sure to follow the inheritance trail, all the way to the parent class. &lt;/p&gt;

&lt;p&gt;That said, an example is always great.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You have a working version of Horizon either on your PC or virtual machine&lt;/li&gt;
&lt;li&gt;You have the latest code from master&lt;/li&gt;
&lt;li&gt;You have created some volumes on the Horizon dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Unit Testing Example
&lt;/h3&gt;

&lt;p&gt;I have horizon installed locally. I have created some volumes on my Horizon dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwwxqrg9w3wu7nl07mcx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwwxqrg9w3wu7nl07mcx.png" alt=" " width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I need to write a test to determine whether the "AttachedTo" column on the Volumes table displays a dash [-], if the volume is not attached to an instance.&lt;/p&gt;

&lt;p&gt;The first thing I need to do is find the code that generates the column on the volumes table.&lt;/p&gt;

&lt;p&gt;You'll find it under&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&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;horizon&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;openstack_dashboard&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dashboards&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The specific class is AttachmentColumn. This is a custom column class for displaying volume attachments. It handles potential edge cases like empty attachments or incomplete server information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AttachmentColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WrappingColumn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Customized column class.

    So it that does complex processing on the attachments
    for a volume instance.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;instance_detail_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;horizon:project:instances:detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_raw_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;
        &lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%(dev)s on %(instance)s&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;attachments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="c1"&gt;# Filter out "empty" attachments which the client returns...
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;att&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;att&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;att&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="c1"&gt;# When a volume is attached it may return the server_id
&lt;/span&gt;            &lt;span class="c1"&gt;# without the server name...
&lt;/span&gt;            &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_attachment_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                           &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_detail_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;device&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt;
            &lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;vals&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;attachments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;safestring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test essentially tests this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;safestring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Collecting the ingredients we need for our test
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Check your Python version (if older, install &lt;a href="https://pypi.org/project/mock/" rel="noopener noreferrer"&gt;mock from PyPI&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;In newer versions, Python 3.3+, unitttest.mock is part of the library by default.&lt;/li&gt;
&lt;li&gt;Think about what you want to test for (testing for None, equality, existence, truthiness,or falsiness).&lt;/li&gt;
&lt;li&gt;In our case, we are testing for None, since the dash [-] translates to None.&lt;/li&gt;
&lt;li&gt;Think about the scope of what you want to test. Do you want to write a test for the entire class? Or just the method? I went with the latter.&lt;/li&gt;
&lt;li&gt;That means that we need to create an instance of the &lt;em&gt;AttachmentColumn&lt;/em&gt; class, which inherits from &lt;em&gt;tables.WrappingColumn&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Let's explore this class more.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;tables&lt;/em&gt; code is on this file path:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&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;horizon&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Go to the imports in the

&lt;code&gt;__init__.py&lt;/code&gt;

file and find &lt;em&gt;WrappingColumn&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;horizon.tables.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WrappingColumn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The WrappingColumn is defined in &lt;em&gt;base.py&lt;/em&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&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;horizon&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;blob&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WrappingColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A column that wraps its contents. Useful for data like UUIDs or names&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;word-break&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;It inherits from &lt;em&gt;Column&lt;/em&gt;, which is also defined in &lt;em&gt;base.py&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;In the

&lt;code&gt;__init__&lt;/code&gt;

method of Column, we see that the only required argument is "transform"
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sortable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allowed_data_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status_choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;display_choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;empty_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;truncate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;link_classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wrap_list&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;form_field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;form_field_attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;update_action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;link_attrs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy_rules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;cell_attributes_getter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The docstrings suggest that is can be a string or callable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A class which represents a single column in a :class:`.DataTable`.

    .. attribute:: transform

        A string or callable. If ``transform`` is a string, it should be the
        name of the attribute on the underlying data class which
        should be displayed in this column. If it is a callable, it
        will be passed the current row&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s data at render-time and should
        return the contents of the cell. Required.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We now have the attribute to use when creating an instance of &lt;em&gt;AttachmentColumn.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Mocking
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;To imitate the functionality of the &lt;em&gt;AttachmentColumn&lt;/em&gt; class, we need to create some &lt;a href="https://docs.python.org/3/library/unittest.mock.html" rel="noopener noreferrer"&gt;mocks&lt;/a&gt;.Think about mocks as "mimics". &lt;/li&gt;
&lt;li&gt;We could mimic a table which is where the column we intend to test lives, for example.&lt;/li&gt;
&lt;li&gt;Mocks also come in handy because Horizon makes API calls to services like Cinder to display volume information. We would need to "mimic" this API calls too.&lt;/li&gt;
&lt;li&gt;To display a volume, for example, we would need to send a request to Cinder, asking for volume information.&lt;/li&gt;
&lt;li&gt;We would also need to "mimic" a volume, in this case, with the attachments attribute being an empty list, since it has no attachments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Writing the Test
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We are going to use the aforementioned unittest.mock library which has a Mock() class to help us in "mimicking"
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_attachment_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;volume_tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AttachmentColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attachments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;volume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_raw_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIsNone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We defined a method called "test_attachment_column"&lt;/li&gt;
&lt;li&gt;We then created an instance of AttachmentColumn. Since the class is contained in the tables module, we prefixed that. If you check the imports, the tables module is imported as volume_tables.&lt;/li&gt;
&lt;li&gt;We then created a mock of our table, request, and volume, with the attachments attribute being an empty list.&lt;/li&gt;
&lt;li&gt;In our code, we use mock.Mock() because we did not explicitly import Mock.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We then called our method, get_raw_data from the AttachmentColumn, passing in our volume as an argument.&lt;/li&gt;
&lt;li&gt;Eventually, we created an assertion, assertIsNone, to confirm that our volume has no attachments, and that translates to 'None', our [-]&lt;/li&gt;
&lt;li&gt;Note that we need to call the method we test (&lt;em&gt;get_raw_data&lt;/em&gt; in our case) and use assert to compare the result with what we expect to get.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Checking the correctness of your test
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We use tox within the OpenStack ecosystem to run tests. You can run this command in horizon's root directory:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tox&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;py3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;  &lt;span class="n"&gt;openstack_dashboard&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dashboards&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="n"&gt;test_attachment_column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If using a different python version, then your command should be
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tox&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;python_version&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;  &lt;span class="n"&gt;openstack_dashboard&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dashboards&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="n"&gt;test_attachment_column&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Your test should pass.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;openstack_dashboard&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dashboards&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;VolumeIndexViewTests&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;test_attachment_column&lt;/span&gt; &lt;span class="n"&gt;PASSED&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You can also edit code in AttachmentColumn and rerun the test command to confirm that the code works.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#if attachments:
&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;safestring&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The test should now fail.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;openstack_dashboard&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dashboards&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;volumes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;VolumeIndexViewTests&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;test_attachment_column&lt;/span&gt; &lt;span class="n"&gt;FAILED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A word on mocks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We could have used Mock() interchangeably with MagicMock(), but the latter is more powerful, with more "bells and whistles" which could break your tests, or cause some to pass/fail due to default MagicMock behaviour.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Forging Forward
&lt;/h2&gt;

&lt;p&gt;There's still a lot to explore regarding unit tests within the OpenStack Horizon project. Some tests use pytest, for example, and others use the &lt;a href="https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch" rel="noopener noreferrer"&gt;patch()&lt;/a&gt; decorator. &lt;/p&gt;

&lt;p&gt;I hope that this blog would go a long way in helping you get started with unit testing in Horizon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://realpython.com/python-unittest/" rel="noopener noreferrer"&gt;Python's unittest: Writing Unit Tests for Your Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/unittest.mock.html" rel="noopener noreferrer"&gt;unittest.mock — mock object library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://realpython.com/python-mock-library/" rel="noopener noreferrer"&gt;Understanding the Python Mock Object Library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mike-diaz006.medium.com/what-i-learned-at-work-this-week-magicmock-61996506bc27" rel="noopener noreferrer"&gt;What I Learned at Work this Week: MagicMock&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>openstack</category>
      <category>outreachy</category>
      <category>cloud</category>
      <category>django</category>
    </item>
    <item>
      <title>OpenStack Horizon Week 1: Project Navigation and Task 1</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Wed, 05 Jun 2024 10:57:47 +0000</pubDate>
      <link>https://forem.com/ndutared/week-1-project-navigation-and-task-1-1bpi</link>
      <guid>https://forem.com/ndutared/week-1-project-navigation-and-task-1-1bpi</guid>
      <description>&lt;p&gt;You might know that I'll be contributing to OpenStack Horizon. If not, you can read all about it in my &lt;a href="https://dev.to/ndutared/my-outreachy-application-journey-28m1"&gt;application journey.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I share my progress this far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Week 1
&lt;/h2&gt;

&lt;p&gt;Week 1 was a roller coaster for me. I spent some time refreshing my Python and Django knowledge, but honestly there was still a lot to learn.&lt;/p&gt;

&lt;p&gt;I spent the first week going through some tutorials, as I had already done the project set up during the contribution phase.&lt;/p&gt;

&lt;p&gt;The tutorials were okay, and I started to understand the project somewhat.&lt;/p&gt;

&lt;p&gt;Truth be told, OpenStack is a mammoth. &lt;/p&gt;

&lt;h3&gt;
  
  
  Task 1
&lt;/h3&gt;

&lt;p&gt;I had my first meeting and it was apparent that we would need Angular to work with Glance, as there had been a transition. I have never written any Angular in my life, so that was quite a punch in the stomach.&lt;/p&gt;

&lt;p&gt;So what next?&lt;/p&gt;

&lt;p&gt;My mentors advised to start with Python/Django, so now the focus moved to Cinder(the initial plan was to work with Glance).&lt;/p&gt;

&lt;p&gt;So, we set up the first task. And I realized that I had not even come across Cinder code even after spending some time going through the code.&lt;/p&gt;

&lt;p&gt;My mentors were kind enough to go point me in the right direction. &lt;/p&gt;

&lt;p&gt;The first task was fixing a really &lt;a href="https://review.opendev.org/c/openstack/horizon/+/921362" rel="noopener noreferrer"&gt;small bug&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blockers
&lt;/h3&gt;

&lt;p&gt;I was excited that my mentors wanted me to start small. My mentor had fixed the bug in like five minutes. I was like, okay, maybe it'll take me an hour tops.&lt;/p&gt;

&lt;p&gt;When I actually sat down to write the code, I was quite lost. It took me quite a while to find the right files.&lt;/p&gt;

&lt;p&gt;I then spent hours going through &lt;em&gt;volumes&lt;/em&gt; code. I went through the classes, trying to understand what each was doing, and noting the ones responsible for the bug I was going to fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Murky waters
&lt;/h3&gt;

&lt;p&gt;Somewhere along the way, I realized that the &lt;em&gt;volumes&lt;/em&gt; code was not 100% mapped to Django file structure. &lt;/p&gt;

&lt;p&gt;I couldn't find the templates responsible for that particular code. And after many hours I was in very muddy waters. I couldn't just figure out the connection between some files.&lt;/p&gt;

&lt;p&gt;I went to bed without any sense of accomplishment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asking for help
&lt;/h3&gt;

&lt;p&gt;I however was honest about not making some connections, and my mentor sent more resources my way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Out of the Woods
&lt;/h3&gt;

&lt;p&gt;It helps to work on code when my mind is settled. So I read through my mentors resources. One of them was a tutorial I already gone through.&lt;/p&gt;

&lt;p&gt;I stepped back and tried to apply my experience getting lost with the resources. I was also able to narrow down to the specific code where the bug was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Submitting my review
&lt;/h2&gt;

&lt;p&gt;It took me a while to remember the steps I needed to take. I had copied and pasted a doc with some steps, but I couldn't trace its source.&lt;/p&gt;

&lt;p&gt;I finally found it, and decided to write down the &lt;a href="https://github.com/AgnesNM/OpenStack-Horizon/blob/main/review.md" rel="noopener noreferrer"&gt;submitting a review steps&lt;/a&gt; for my future self, and hopefully, for someone else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forging onward
&lt;/h2&gt;

&lt;p&gt;For now, there's still a long way to go, and I hope for many good days, without getting too lost in the woods. &lt;/p&gt;

&lt;h2&gt;
  
  
  Speaking OpenStack if you are coming from AWS
&lt;/h2&gt;

&lt;p&gt;I've found it helpful to think about OpenStack in terms of AWS, since that's what I understand a little better. So here is "AWS-translated OpenStack"&lt;/p&gt;

&lt;p&gt;There's much more, of course, these are just some main concepts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnd9q61qleyhvmujsshcq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnd9q61qleyhvmujsshcq.png" alt=" " width="800" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cover Image credits: &lt;a href="https://bit.ly/4aNF3zB" rel="noopener noreferrer"&gt;https://bit.ly/4aNF3zB&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>horizon</category>
      <category>openstack</category>
      <category>cinder</category>
    </item>
    <item>
      <title>My Outreachy Application Journey</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Wed, 15 May 2024 11:32:27 +0000</pubDate>
      <link>https://forem.com/ndutared/my-outreachy-application-journey-28m1</link>
      <guid>https://forem.com/ndutared/my-outreachy-application-journey-28m1</guid>
      <description>&lt;p&gt;I first learned about paid internships like Outreachy via Twitter. If you ask me, I look at a program like Outreachy as an apprenticeship.&lt;/p&gt;

&lt;p&gt;Anyway, it was past the deadline and I subscribed to the newsletter and made a promise to myself to apply during the next cohort. This was 2023.&lt;/p&gt;

&lt;p&gt;I didn't get in, but I was not discouraged. I'd apply again. &lt;/p&gt;

&lt;p&gt;In this post, I'll share the things I did differently the second time. Please note, I don't know if that's why I was chosen, that would ultimately be the organizers decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second attempt
&lt;/h2&gt;

&lt;p&gt;So, I sat back and decided to try a different approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  I started way early...
&lt;/h3&gt;

&lt;p&gt;I knew from my previous application that there would be lots of writing to answer the essay questions. Since the questions are the same, I copied them on a Google Doc and wrote my answers even before the application date was announced.&lt;/p&gt;

&lt;h3&gt;
  
  
  I prioritized the application
&lt;/h3&gt;

&lt;p&gt;I spent 30 minutes to an hour everyday for two weeks writing my answers since I was working a full-time job.&lt;/p&gt;

&lt;p&gt;This meant that my answers were ready to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  I read about other people's experiences
&lt;/h3&gt;

&lt;p&gt;I spent some time reading through other people's experiences just to have some perspectives about application and choosing projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  I reached out to previous interns
&lt;/h3&gt;

&lt;p&gt;I had met someone who had done Outreachy before, so I asked for her input. This came in handy during the contribution phase.&lt;/p&gt;

&lt;h3&gt;
  
  
  I chose a project that was in line with my skills and that was interesting too
&lt;/h3&gt;

&lt;p&gt;When the applications were opened, I spent three days going through the projects and narrowed down to three. Remember I had a full-time job, so I didn't have too much time.&lt;/p&gt;

&lt;p&gt;I eventually settled on one project, as I wanted to focus in case I was chosen.&lt;/p&gt;

&lt;h3&gt;
  
  
  I sent my application in early
&lt;/h3&gt;

&lt;p&gt;Since I already had my answers ready, I was done with the application early enough, even when there were many days left on to the deadline.&lt;/p&gt;

&lt;h3&gt;
  
  
  I sent my application, prayed, and waited
&lt;/h3&gt;

&lt;p&gt;I sent the initial application, prayed and waited. I also kept taking notes about my next application, especially from Joan, who had participated before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;Time flew by. I eventually got the acceptance email.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famkrxgkr51ksvsid62fw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famkrxgkr51ksvsid62fw.jpg" alt="Image description" width="800" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  I started going through the project's docs, project files, issues and bugs
&lt;/h3&gt;

&lt;p&gt;I spent the first two or three days going through the project's docs and codebase.&lt;/p&gt;

&lt;p&gt;It was a bit overwhelming, but it gave me an idea of what the project was about.&lt;/p&gt;

&lt;p&gt;Having AWS knowledge came in handy as I could translate the knowledge from AWS to OpenStack concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  I reached out to the project mentor
&lt;/h3&gt;

&lt;p&gt;I reached out to the project mentor. I told her that I had been accepted to contribute to OpenStack, and shared the progress I had made.&lt;/p&gt;

&lt;p&gt;I then asked her for guidance on how I could go about contributing. I was looking to make a very simple contribution as I didn't have too much time, again, I was working full-time.&lt;/p&gt;

&lt;p&gt;This helped me narrow my focus, as I could concentrate on what was important without being overwhelmed.&lt;/p&gt;

&lt;h3&gt;
  
  
  I started contributing
&lt;/h3&gt;

&lt;p&gt;Tatiana was very helpful in providing the resources I needed. I updated her on my progress every few days, including when I was stuck, and any workarounds I came up with.&lt;/p&gt;

&lt;p&gt;The contributing phase was about three hours every day, so I would wake up really early so that I could have at least three hours before work.&lt;/p&gt;

&lt;h3&gt;
  
  
  I kept my mentor updated
&lt;/h3&gt;

&lt;p&gt;As aforementioned, I kept my mentor updated on my progress. I also incorporated any feedback she gave.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Application
&lt;/h2&gt;

&lt;p&gt;It was such a joy when my contribution got &lt;a href="https://review.opendev.org/c/openstack/horizon/+/914421" rel="noopener noreferrer"&gt;merged&lt;/a&gt;. It was my first ever merged open source contribution.&lt;/p&gt;

&lt;p&gt;I had tried before, but my contribution was not merged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finishing up
&lt;/h3&gt;

&lt;p&gt;I finished up my final application, after a go ahead from my mentor.&lt;/p&gt;

&lt;h3&gt;
  
  
  I then submitted, prayed and waited
&lt;/h3&gt;

&lt;p&gt;I submitted my final application two days before the deadline. I then submitted, prayed and waited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acceptance
&lt;/h2&gt;

&lt;p&gt;I was so anxious as I waited for the results. I refreshed my email like every five minutes.&lt;/p&gt;

&lt;p&gt;But finally, I got the acceptance email. I was literally in the clouds with joy.&lt;/p&gt;

&lt;p&gt;I did my victory dance too...and thanked God for his mercies...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faenbc4oltiy8btmzvk2y.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faenbc4oltiy8btmzvk2y.gif" alt="Image description" width="800" height="928"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Image source: &lt;a href="https://www.furaffinity.net/view/49164924/" rel="noopener noreferrer"&gt;https://www.furaffinity.net/view/49164924/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I signed the contract very fast...and called my friends to tell share the good news. &lt;/p&gt;

&lt;p&gt;I also reached put to my mentor Tatiana again and thanked her for her support. &lt;/p&gt;

&lt;p&gt;I also told her what I had in mind as prep work, as the contribution phase began later in the month.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prep work
&lt;/h3&gt;

&lt;p&gt;Tatiana guided me on the next steps, and the resources to look at.&lt;/p&gt;

&lt;p&gt;I'm currently knee deep polishing up any dust off my Python/Django knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The future
&lt;/h2&gt;

&lt;p&gt;I'm going to do my best to understand the project and deliver. &lt;/p&gt;

&lt;p&gt;I'm also taking all the help I can get from my mentor, the OpenStack community, and the larger tech community.&lt;/p&gt;

&lt;p&gt;I hope to staying humble, teachable, and have lots of fun!!!&lt;/p&gt;

&lt;p&gt;And as you can imagine, I'll be praying lots too :)&lt;/p&gt;

&lt;p&gt;Let me know if you have any questions. &lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>cloud</category>
      <category>python</category>
    </item>
    <item>
      <title>The WWCode Days of Code Challenge</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Mon, 08 Jan 2024 06:33:10 +0000</pubDate>
      <link>https://forem.com/ndutared/the-wwcode-days-of-code-challenge-771</link>
      <guid>https://forem.com/ndutared/the-wwcode-days-of-code-challenge-771</guid>
      <description>&lt;p&gt;Staying consistent might not be easy at the start. Here's a challenge by #WomenWhoCodeAccra to help you stay on track.&lt;/p&gt;

&lt;p&gt;✨ Join Women Who Code and our community of coders to level up your skills during our online Days of Code Challenge! Learn at your own pace with our fun, supportive community. Sign up starting today to begin the challenge on Monday, January 8th. ✨&lt;/p&gt;

&lt;p&gt;The WWCode Days of Code Challenge is hosted by the WWCode Technical Track communities and is an ongoing, self-directed and self-paced learning experience that will allow you to start the New Year with a commitment to level up your skills!&lt;/p&gt;

&lt;p&gt;Finding time to work on your skills or projects — or going at it alone — can be challenging. The WWCode Days of Code Challenge will give you the space and encouragement to improve your skills, finish projects, help other coders and learn from other technologists.&lt;/p&gt;

&lt;p&gt;👩‍💻 Who this challenge is for:&lt;/p&gt;

&lt;p&gt;Any technologist who wants to work on their coding skills in a supportive, interactive community.&lt;br&gt;
All learning and coding levels.&lt;br&gt;
All tech stacks.&lt;br&gt;
💖 Why you should join:&lt;/p&gt;

&lt;p&gt;Flexibility: Learn and work online at your own pace and level — and in your own tech stack.&lt;br&gt;
Community: Meet, learn from, and mentor other coders who share your passion for tech.&lt;br&gt;
New Year, New You: Start the New Year strong by building your network and knowledge to help you excel in your career in 2024!&lt;br&gt;
📅 Join Today to Begin the Challenge Monday, January 8th&lt;/p&gt;

&lt;p&gt;The challenge will happen online. Here’s how to join:&lt;/p&gt;

&lt;p&gt;Register for the challenge on Hopin.&lt;br&gt;
Attend the Opening Ceremony on January 8th at 11:00 AM EST, or access the recording afterwards, to learn all about how to participate in the program.&lt;/p&gt;

&lt;p&gt;Start the challenge on Monday, January 8th (or your chosen start date)!&lt;br&gt;
Code every day for your selected number of days — 7, 14, 30, 60, or 100.&lt;br&gt;
Share your progress with the community at least once per week.&lt;br&gt;
We look forward to learning with you!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>coding</category>
      <category>codingchalle</category>
      <category>consistency</category>
    </item>
    <item>
      <title>Setting up PostgreSQL on a Django/Python project</title>
      <dc:creator>Abby Nduta</dc:creator>
      <pubDate>Sun, 06 Aug 2023 08:59:04 +0000</pubDate>
      <link>https://forem.com/ndutared/setting-up-postgresql-on-a-djangopython-project-2pi9</link>
      <guid>https://forem.com/ndutared/setting-up-postgresql-on-a-djangopython-project-2pi9</guid>
      <description>&lt;p&gt;Postgres is a popular relational database that you can use in your Python/Django project. Setting it up however, can be a little overwhelming.&lt;/p&gt;

&lt;p&gt;Graciously, with Django models, you don’t have to write too many SQL queries to create your database, tables and even add data on your psql shell.&lt;/p&gt;

&lt;p&gt;Here is your step by step summary to install and set up PostgreSQL on Ubuntu 22.04:&lt;/p&gt;

&lt;h3&gt;
  
  
  Check whether Postgres is installed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open a terminal or command prompt.&lt;/li&gt;
&lt;li&gt;Run the following command:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; psql &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The version information will be displayed in the output.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Install Postgres
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;postgresql postgresql-contrib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start the PostgreSQL service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start postgresql.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Switch over to the postgres account
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Access the psql shell
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a superuser role with login privileges, and password
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; CREATE ROLE &amp;lt;name_of_role&amp;gt; LOGIN PASSWORD &amp;lt;password_as_a string&amp;gt; BYPASSRLS&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create database
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;CREATE DATABASE &amp;lt;database_name&amp;gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Grant privileges on the database
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON DATABASE &amp;lt;database_name&amp;gt; TO &amp;lt;created_superuser&amp;gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connect to the database (outside of psql and user postgres)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-U&lt;/span&gt; payments_admin &lt;span class="nt"&gt;-h&lt;/span&gt; localhost &lt;span class="nt"&gt;-p&lt;/span&gt; 5433 &lt;span class="nt"&gt;-d&lt;/span&gt; payments&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, you are now ready to start going migrations to your database, and create tables based on your Django models.&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
