summaryrefslogtreecommitdiff
path: root/doc/source/contributor/quickstart.rst
blob: b464a9e03f9ccda4c6251be7d14c1358016bf33a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
.. _quickstart:

==========
Quickstart
==========

..  Note ::

    This section has been tested for Horizon on Ubuntu (16.04-64) and RPM-based
    (RHEL 7.x) distributions. Feel free to add notes and any changes according
    to your experiences or operating system.

Linux Systems
=============

Install the prerequisite packages.

On Ubuntu

.. code-block:: console

    $ sudo apt-get install git python-pip

On RPM-based distributions (e.g., Fedora/RHEL/CentOS/Scientific Linux)

.. code-block:: console

    $ sudo yum install gcc git-core python-devel python-virtualenv openssl-devel libffi-devel which

.. note::

    Some tests rely on the Chrome web browser being installed. While the above
    requirements will allow you to run and manually test Horizon, you will
    need to install Chrome to run the full test suite.

Setup
=====

To begin setting up a Horizon development environment simply clone the Horizon
git repository from https://git.openstack.org/cgit/openstack/horizon

.. code-block:: console

    $ git clone https://git.openstack.org/openstack/horizon

Next you will need to configure Horizon by adding a ``local_settings.py`` file.
A good starting point is to use the example config with the following command,
from within the ``horizon`` directory.

.. code-block:: console

    $ cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py

Horizon connects to the rest of OpenStack via a Keystone service catalog. By
default Horizon looks for an endpoint at ``http://localhost:5000/v2.0``; this
can be customised by modifying the ``OPENSTACK_HOST`` and
``OPENSTACK_KEYSTONE_URL`` values in
``openstack_dashboard/local/local_settings.py``

.. note::

    The DevStack project (http://devstack.org/) can be used to install
    an OpenStack development environment from scratch. For a local.conf that
    enables most services that Horizon supports managing, see
    :ref:`local-conf`

Horizon uses ``tox`` to manage virtual environments for testing and other
development tasks. You can install it with

.. code-block:: console

    $ pip install tox

The ``tox`` environments provide wrappers around ``manage.py``. For more
information on ``manage.py``, which is a Django command, see
https://docs.djangoproject.com/en/dev/ref/django-admin/

To start the Horizon development server use the command below

.. code-block:: console

    $ tox -e runserver

.. note::

    The default port for runserver is 8000 which is already consumed by
    heat-api-cfn in DevStack. If running in DevStack
    ``tox -e runserver -- localhost:9000`` will start the test server at
    ``http://localhost:9000``

Once the Horizon server is running, point a web browser to ``http://localhost``
or to the IP and port the server is listening for. Enter your Keystone
credentials, log in and you'll be presented with the Horizon dashboard.
Congratulations!

Managing Settings
=================

You can save changes you made to
``openstack_dashboard/local/local_settings.py`` with the following command:

.. code-block:: console

    $ python manage.py migrate_settings --gendiff

.. note::

    This creates a ``local_settings.diff`` file which is a diff between
    ``local_settings.py`` and ``local_settings.py.example``

If you upgrade Horizon, you might need to update your
``openstack_dashboard/local/local_settings.py`` file with new parameters from
``openstack_dashboard/local/local_settings.py.example`` to do so, first update
Horizon

.. code-block:: console

    $ git remote update && git pull --ff-only origin master

Then update your  ``openstack_dashboard/local/local_settings.py`` file

.. code-block:: console

    $ mv openstack_dashboard/local/local_settings.py openstack_dashboard/local/local_settings.py.old
    $ python manage.py migrate_settings

.. note::

    This applies ``openstack_dashboard/local/local_settings.diff`` on
    ``openstack_dashboard/local/local_settings.py.example`` to regenerate an
    ``openstack_dashboard/local/local_settings.py`` file.
    The migration can sometimes have difficulties to migrate some settings, if
    this happens you will be warned with a conflict message pointing to an
    ``openstack_dashboard/local/local_settings.py_Some_DateTime.rej`` file.
    In this file, you will see the lines which could not be automatically
    changed and you will have to redo only these few changes manually instead
    of modifying the full
    ``openstack_dashboard/local/local_settings.py.example`` file.

When all settings have been migrated, it is safe to regenerate a clean diff in
order to prevent Conflicts for future migrations

.. code-block:: console

    $ mv openstack_dashboard/local/local_settings.diff openstack_dashboard/local/local_settings.diff.old
    $ python manage.py migrate_settings --gendiff

Editing Horizon's Source
========================

Although DevStack installs and configures an instance of Horizon when running
stack.sh, the preferred development setup follows the instructions above on the
server/VM running DevStack. There are several advantages to maintaining a
separate copy of the Horizon repo, rather than editing the DevStack installed
copy.

- Source code changes aren't as easily lost when running ``unstack.sh`` /
  ``stack.sh``
- The development server picks up source code changes while still running.
- Log messages and print statements go directly to the console.
- Debugging with ``pdb`` becomes much simpler to interact with.

.. note::

  To ensure that JS and CSS changes are picked up without a server restart, you
  can disable compression with ``COMPRESS_ENABLED = False`` in your local
  settings file.

Horizon's Structure
===================

This project is a bit different from other OpenStack projects in that it has
two very distinct components underneath it: ``horizon``, and
``openstack_dashboard``.

The ``horizon`` directory holds the generic libraries and components that can
be used in any Django project.

The ``openstack_dashboard`` directory contains a reference Django project that
uses ``horizon``.

For development, both pieces share an environment which (by default) is
built with the ``tools/install_venv.py`` script. That script creates a
virtualenv and installs all the necessary packages.

If dependencies are added to either ``horizon`` or ``openstack_dashboard``,
they should be added to ``requirements.txt``.

Project Structure
=================

Dashboard configuration
-----------------------

To add a new dashboard to your project, you need to add a configuration file to
``openstack_dashboard/local/enabled`` directory. For more information on this,
see :ref:`pluggable-settings-label`.

URLs
----

Then you add a single line to your project's ``urls.py``

.. code-block:: python

    url(r'', include(horizon.urls)),

Those urls are automatically constructed based on the registered Horizon apps.
If a different URL structure is desired it can be constructed by hand.

Templates
---------

Pre-built template tags generate navigation. In your ``nav.html``
template you might have the following

.. code-block:: htmldjango

    {% load horizon %}

    <div class='nav'>
      {% horizon_main_nav %}
    </div>

And in your ``sidebar.html`` you might have

.. code-block:: htmldjango

    {% load horizon %}

    <div class='sidebar'>
      {% horizon_dashboard_nav %}
    </div>

These template tags are aware of the current "active" dashboard and panel
via template context variables and will render accordingly.

Application Design
==================

Structure
---------

An application would have the following structure (we'll use project as
an example)

.. code-block:: console

    project/
    |---__init__.py
    |---dashboard.py <-----Registers the app with Horizon and sets dashboard properties
    |---overview/
    |---images/
        |-- images
        |-- __init__.py
        |---panel.py <-----Registers the panel in the app and defines panel properties
        |-- snapshots/
        |-- templates/
        |-- tests.py
        |-- urls.py
        |-- views.py
        ...
    ...

Dashboard Classes
-----------------

Inside of ``dashboard.py`` you would have a class definition and the
registration process

.. code-block:: python

    import horizon

    ....
    # ObjectStorePanels is an example for a PanelGroup
    # for panel classes in general, see below
    class ObjectStorePanels(horizon.PanelGroup):
        slug = "object_store"
        name = _("Object Store")
        panels = ('containers',)


    class Project(horizon.Dashboard):
        name = _("Project") # Appears in navigation
        slug = "project"    # Appears in URL
        # panels may be strings or refer to classes, such as
        # ObjectStorePanels
        panels = (BasePanels, NetworkPanels, ObjectStorePanels)
        default_panel = 'overview'
        ...

    horizon.register(Project)

Panel Classes
-------------

To connect a :class:`~horizon.Panel` with a :class:`~horizon.Dashboard` class
you register it in a ``panel.py`` file

.. code-block:: python

    import horizon

    from openstack_dashboard.dashboards.project import dashboard


    class Images(horizon.Panel):
        name = "Images"
        slug = 'images'
        permissions = ('openstack.roles.admin', 'openstack.service.image')
        policy_rules = (('endpoint', 'endpoint:rule'),)

    # You could also register your panel with another application's dashboard
    dashboard.Project.register(Images)

By default a :class:`~horizon.Panel` class looks for a ``urls.py`` file in the
same directory as ``panel.py`` to include in the rollup of url patterns from
panels to dashboards to Horizon, resulting in a wholly extensible, configurable
URL structure.

Policy rules are defined in ``horizon/openstack_dashboard/conf/``. Permissions
are inherited from Keystone and take either the form
'openstack.roles.role_name' or 'openstack.services.service_name' for the user's
roles in keystone and the services in their service catalog.