Add general storage parameters to node info call
[ganeti-local] / doc / design-monitoring-agent.rst
1 =======================
2 Ganeti monitoring agent
3 =======================
4
5 .. contents:: :depth: 4
6
7 This is a design document detailing the implementation of a Ganeti
8 monitoring agent report system, that can be queried by a monitoring
9 system to calculate health information for a Ganeti cluster.
10
11 Current state and shortcomings
12 ==============================
13
14 There is currently no monitoring support in Ganeti. While we don't want
15 to build something like Nagios or Pacemaker as part of Ganeti, it would
16 be useful if such tools could easily extract information from a Ganeti
17 machine in order to take actions (example actions include logging an
18 outage for future reporting or alerting a person or system about it).
19
20 Proposed changes
21 ================
22
23 Each Ganeti node should export a status page that can be queried by a
24 monitoring system. Such status page will be exported on a network port
25 and will be encoded in JSON (simple text) over HTTP.
26
27 The choice of JSON is obvious as we already depend on it in Ganeti and
28 thus we don't need to add extra libraries to use it, as opposed to what
29 would happen for XML or some other markup format.
30
31 Location of agent report
32 ------------------------
33
34 The report will be available from all nodes, and be concerned for all
35 node-local resources. This allows more real-time information to be
36 available, at the cost of querying all nodes.
37
38 Information reported
39 --------------------
40
41 The monitoring agent system will report on the following basic information:
42
43 - Instance status
44 - Instance disk status
45 - Status of storage for instances
46 - Ganeti daemons status, CPU usage, memory footprint
47 - Hypervisor resources report (memory, CPU, network interfaces)
48 - Node OS resources report (memory, CPU, network interfaces)
49 - Information from a plugin system
50
51 Format of the report
52 --------------------
53
54 The report of the will be in JSON format, and it will present an array
55 of report objects.
56 Each report object will be produced by a specific data collector.
57 Each report object includes some mandatory fields, to be provided by all
58 the data collectors:
59
60 ``name``
61   The name of the data collector that produced this part of the report.
62   It is supposed to be unique inside a report.
63
64 ``version``
65   The version of the data collector that produces this part of the
66   report. Built-in data collectors (as opposed to those implemented as
67   plugins) should have "B" as the version number.
68
69 ``format_version``
70   The format of what is represented in the "data" field for each data
71   collector might change over time. Every time this happens, the
72   format_version should be changed, so that who reads the report knows
73   what format to expect, and how to correctly interpret it.
74
75 ``timestamp``
76   The time when the reported data were gathered. It has to be expressed
77   in nanoseconds since the unix epoch (0:00:00 January 01, 1970). If not
78   enough precision is available (or needed) it can be padded with
79   zeroes. If a report object needs multiple timestamps, it can add more
80   and/or override this one inside its own "data" section.
81
82 ``category``
83   A collector can belong to a given category of collectors (e.g.: storage
84   collectors, daemon collector). This means that it will have to provide a
85   minumum set of prescribed fields, as documented for each category.
86   This field will contain the name of the category the collector belongs to,
87   if any, or just the ``null`` value.
88
89 ``kind``
90   Two kinds of collectors are possible:
91   `Performance reporting collectors`_ and `Status reporting collectors`_.
92   The respective paragraphs will describe them and the value of this field.
93
94 ``data``
95   This field contains all the data generated by the specific data collector,
96   in its own independently defined format. The monitoring agent could check
97   this syntactically (according to the JSON specifications) but not
98   semantically.
99
100 Here follows a minimal example of a report::
101
102   [
103   {
104       "name" : "TheCollectorIdentifier",
105       "version" : "1.2",
106       "format_version" : 1,
107       "timestamp" : 1351607182000000000,
108       "category" : null,
109       "kind" : 0,
110       "data" : { "plugin_specific_data" : "go_here" }
111   },
112   {
113       "name" : "AnotherDataCollector",
114       "version" : "B",
115       "format_version" : 7,
116       "timestamp" : 1351609526123854000,
117       "category" : "storage",
118       "kind" : 1,
119       "data" : { "status" : { "code" : 1,
120                               "message" : "Error on disk 2"
121                             },
122                  "plugin_specific" : "data",
123                  "some_late_data" : { "timestamp" : 1351609526123942720,
124                                       ...
125                                     }
126                }
127   }
128   ]
129
130 Performance reporting collectors
131 ++++++++++++++++++++++++++++++++
132
133 These collectors only provide data about some component of the system, without
134 giving any interpretation over their meaning.
135
136 The value of the ``kind`` field of the report will be ``0``.
137
138 Status reporting collectors
139 +++++++++++++++++++++++++++
140
141 These collectors will provide information about the status of some
142 component of ganeti, or managed by ganeti.
143
144 The value of their ``kind`` field will be ``1``.
145
146 The rationale behind this kind of collectors is that there are some situations
147 where exporting data about the underlying subsystems would expose potential
148 issues. But if Ganeti itself is able (and going) to fix the problem, conflicts
149 might arise between Ganeti and something/somebody else trying to fix the same
150 problem.
151 Also, some external monitoring systems might not be aware of the internals of a
152 particular subsystem (e.g.: DRBD) and might only exploit the high level
153 response of its data collector, alerting an administrator if anything is wrong.
154 Still, completely hiding the underlying data is not a good idea, as they might
155 still be of use in some cases. So status reporting plugins will provide two
156 output modes: one just exporting a high level information about the status,
157 and one also exporting all the data they gathered.
158 The default output mode will be the status-only one. Through a command line
159 parameter (for stand-alone data collectors) or through the HTTP request to the
160 monitoring agent
161 (when collectors are executed as part of it) the verbose output mode providing
162 all the data can be selected.
163
164 When exporting just the status each status reporting collector will provide,
165 in its ``data`` section, at least the following field:
166
167 ``status``
168   summarizes the status of the component being monitored and consists of two
169   subfields:
170
171   ``code``
172     It assumes a numeric value, encoded in such a way to allow using a bitset
173     to easily distinguish which states are currently present in the whole
174     cluster. If the bitwise OR of all the ``status`` fields is 0, the cluster
175     is completely healty.
176     The status codes are as follows:
177
178     ``0``
179       The collector can determine that everything is working as
180       intended.
181
182     ``1``
183       Something is temporarily wrong but it is being automatically fixed by
184       Ganeti.
185       There is no need of external intervention.
186
187     ``2``
188       The collector has failed to understand whether the status is good or
189       bad. Further analysis is required. Interpret this status as a
190       potentially dangerous situation.
191
192     ``4``
193       The collector can determine that something is wrong and Ganeti has no
194       way to fix it autonomously. External intervention is required.
195
196   ``message``
197     A message to better explain the reason of the status.
198     The exact format of the message string is data collector dependent.
199
200     The field is mandatory, but the content can be an empty string if the
201     ``code`` is ``0`` (working as intended) or ``1`` (being fixed
202     automatically).
203
204     If the status code is ``2``, the message should specify what has gone
205     wrong.
206     If the status code is ``4``, the message shoud explain why it was not
207     possible to determine a proper status.
208
209 The ``data`` section will also contain all the fields describing the gathered
210 data, according to a collector-specific format.
211
212 Instance status
213 +++++++++++++++
214
215 At the moment each node knows which instances are running on it, which
216 instances it is primary for, but not the cause why an instance might not
217 be running. On the other hand we don't want to distribute full instance
218 "admin" status information to all nodes, because of the performance
219 impact this would have.
220
221 As such we propose that:
222
223 - Any operation that can affect instance status will have an optional
224   "reason" attached to it (at opcode level). This can be used for
225   example to distinguish an admin request, from a scheduled maintenance
226   or an automated tool's work. If this reason is not passed, Ganeti will
227   just use the information it has about the source of the request.
228   This reason information will be structured according to the
229   :doc:`Ganeti reason trail <design-reason-trail>` design document.
230 - RPCs that affect the instance status will be changed so that the
231   "reason" and the version of the config object they ran on is passed to
232   them. They will then export the new expected instance status, together
233   with the associated reason and object version to the status report
234   system, which then will export those themselves.
235
236 Monitoring and auditing systems can then use the reason to understand
237 the cause of an instance status, and they can use the timestamp to
238 understand the freshness of their data even in the absence of an atomic
239 cross-node reporting: for example if they see an instance "up" on a node
240 after seeing it running on a previous one, they can compare these values
241 to understand which data is freshest, and repoll the "older" node. Of
242 course if they keep seeing this status this represents an error (either
243 an instance continuously "flapping" between nodes, or an instance is
244 constantly up on more than one), which should be reported and acted
245 upon.
246
247 The instance status will be on each node, for the instances it is
248 primary for, and its ``data`` section of the report will contain a list
249 of instances, named ``instances``, with at least the following fields for
250 each instance:
251
252 ``name``
253   The name of the instance.
254
255 ``uuid``
256   The UUID of the instance (stable on name change).
257
258 ``admin_state``
259   The status of the instance (up/down/offline) as requested by the admin.
260
261 ``actual_state``
262   The actual status of the instance. It can be ``up``, ``down``, or
263   ``hung`` if the instance is up but it appears to be completely stuck.
264
265 ``uptime``
266   The uptime of the instance (if it is up, "null" otherwise).
267
268 ``mtime``
269   The timestamp of the last known change to the instance state.
270
271 ``state_reason``
272   The last known reason for state change of the instance, described according
273   to the JSON representation of a reason trail, as detailed in the :doc:`reason
274   trail design document <design-reason-trail>`.
275
276 ``status``
277   It represents the status of the instance, and its format is the same as that
278   of the ``status`` field of `Status reporting collectors`_.
279
280 Each hypervisor should provide its own instance status data collector, possibly
281 with the addition of more, specific, fields.
282 The ``category`` field of all of them will be ``instance``.
283 The ``kind`` field will be ``1``.
284
285 Note that as soon as a node knows it's not the primary anymore for an
286 instance it will stop reporting status for it: this means the instance
287 will either disappear, if it has been deleted, or appear on another
288 node, if it's been moved.
289
290 The ``code`` of the ``status`` field of the report of the Instance status data
291 collector will be:
292
293 ``0``
294   if ``status`` is ``0`` for all the instances it is reporting about.
295
296 ``1``
297   otherwise.
298
299 Storage collectors
300 ++++++++++++++++++
301
302 The storage collectors will be a series of data collectors
303 that will gather data about storage for the current node. The collection
304 will be performed at different granularity and abstraction levels, from
305 the physical disks, to partitions, logical volumes and to the specific
306 storage types used by Ganeti itself (drbd, rbd, plain, file).
307
308 The ``name`` of each of these collector will reflect what storage type each of
309 them refers to.
310
311 The ``category`` field of these collector will be ``storage``.
312
313 The ``kind`` field will depend on the specific collector.
314
315 Each ``storage`` collector's ``data`` section will provide collector-specific
316 fields.
317
318 The various storage collectors will provide keys to join the data they provide,
319 in order to allow the user to get a better understanding of the system. E.g.:
320 through device names, or instance names.
321
322 Diskstats collector
323 *******************
324
325 This storage data collector will gather information about the status of the
326 disks installed in the system, as listed in the /proc/diskstats file. This means
327 that not only physical hard drives, but also ramdisks and loopback devices will
328 be listed.
329
330 Its ``kind`` in the report will be ``0`` (`Performance reporting collectors`_).
331
332 Its ``category`` field in the report will contain the value ``storage``.
333
334 When executed in verbose mode, the ``data`` section of the report of this
335 collector will be a list of items, each representing one disk, each providing
336 the following fields:
337
338 ``major``
339   The major number of the device.
340
341 ``minor``
342   The minor number of the device.
343
344 ``name``
345   The name of the device.
346
347 ``readsNum``
348   This is the total number of reads completed successfully.
349
350 ``mergedReads``
351   Reads which are adjacent to each other may be merged for efficiency. Thus
352   two 4K reads may become one 8K read before it is ultimately handed to the
353   disk, and so it will be counted (and queued) as only one I/O. This field
354   specifies how often this was done.
355
356 ``secRead``
357   This is the total number of sectors read successfully.
358
359 ``timeRead``
360   This is the total number of milliseconds spent by all reads.
361
362 ``writes``
363   This is the total number of writes completed successfully.
364
365 ``mergedWrites``
366   Writes which are adjacent to each other may be merged for efficiency. Thus
367   two 4K writes may become one 8K read before it is ultimately handed to the
368   disk, and so it will be counted (and queued) as only one I/O. This field
369   specifies how often this was done.
370
371 ``secWritten``
372   This is the total number of sectors written successfully.
373
374 ``timeWrite``
375   This is the total number of milliseconds spent by all writes.
376
377 ``ios``
378   The number of I/Os currently in progress.
379   The only field that should go to zero, it is incremented as requests are
380   given to appropriate struct request_queue and decremented as they finish.
381
382 ``timeIO``
383   The number of milliseconds spent doing I/Os. This field increases so long
384   as field ``IOs`` is nonzero.
385
386 ``wIOmillis``
387   The weighted number of milliseconds spent doing I/Os.
388   This field is incremented at each I/O start, I/O completion, I/O merge,
389   or read of these stats by the number of I/Os in progress (field ``IOs``)
390   times the number of milliseconds spent doing I/O since the last update of
391   this field. This can provide an easy measure of both I/O completion time
392   and the backlog that may be accumulating.
393
394 Logical Volume collector
395 ************************
396
397 This data collector will gather information about the attributes of logical
398 volumes present in the system.
399
400 Its ``kind`` in the report will be ``0`` (`Performance reporting collectors`_).
401
402 Its ``category`` field in the report will contain the value ``storage``.
403
404 The ``data`` section of the report of this collector will be a list of items,
405 each representing one logical volume and providing the following fields:
406
407 ``uuid``
408   The UUID of the logical volume.
409
410 ``name``
411   The name of the logical volume.
412
413 ``attr``
414   The attributes of the logical volume.
415
416 ``major``
417   Persistent major number or -1 if not persistent.
418
419 ``minor``
420   Persistent minor number or -1 if not persistent.
421
422 ``kernel_major``
423   Currently assigned major number or -1 if LV is not active.
424
425 ``kernel_minor``
426   Currently assigned minor number or -1 if LV is not active.
427
428 ``size``
429   Size of LV in bytes.
430
431 ``seg_count``
432   Number of segments in LV.
433
434 ``tags``
435   Tags, if any.
436
437 ``modules``
438   Kernel device-mapper modules required for this LV, if any.
439
440 ``vg_uuid``
441   Unique identifier of the volume group.
442
443 ``vg_name``
444   Name of the volume group.
445
446 ``segtype``
447   Type of LV segment.
448
449 ``seg_start``
450   Offset within the LVto the start of the segment in bytes.
451
452 ``seg_start_pe``
453   Offset within the LV to the start of the segment in physical extents.
454
455 ``seg_size``
456   Size of the segment in bytes.
457
458 ``seg_tags``
459   Tags for the segment, if any.
460
461 ``seg_pe_ranges``
462   Ranges of Physical Extents of underlying devices in lvs command line format.
463
464 ``devices``
465   Underlying devices used with starting extent numbers.
466
467 ``instance``
468   The name of the instance this LV is used by.
469
470 DRBD status
471 ***********
472
473 This data collector will run only on nodes where DRBD is actually
474 present and it will gather information about DRBD devices.
475
476 Its ``kind`` in the report will be ``1`` (`Status reporting collectors`_).
477
478 Its ``category`` field in the report will contain the value ``storage``.
479
480 When executed in verbose mode, the ``data`` section of the report of this
481 collector will provide the following fields:
482
483 ``versionInfo``
484   Information about the DRBD version number, given by a combination of
485   any (but at least one) of the following fields:
486
487   ``version``
488     The DRBD driver version.
489
490   ``api``
491     The API version number.
492
493   ``proto``
494     The protocol version.
495
496   ``srcversion``
497     The version of the source files.
498
499   ``gitHash``
500     Git hash of the source files.
501
502   ``buildBy``
503     Who built the binary, and, optionally, when.
504
505 ``device``
506   A list of structures, each describing a DRBD device (a minor) and containing
507   the following fields:
508
509   ``minor``
510     The device minor number.
511
512   ``connectionState``
513     The state of the connection. If it is "Unconfigured", all the following
514     fields are not present.
515
516   ``localRole``
517     The role of the local resource.
518
519   ``remoteRole``
520     The role of the remote resource.
521
522   ``localState``
523     The status of the local disk.
524
525   ``remoteState``
526     The status of the remote disk.
527
528   ``replicationProtocol``
529     The replication protocol being used.
530
531   ``ioFlags``
532     The input/output flags.
533
534   ``perfIndicators``
535     The performance indicators. This field will contain the following
536     sub-fields:
537
538     ``networkSend``
539       KiB of data sent on the network.
540
541     ``networkReceive``
542       KiB of data received from the network.
543
544     ``diskWrite``
545       KiB of data written on local disk.
546
547     ``diskRead``
548       KiB of date read from the local disk.
549
550     ``activityLog``
551       Number of updates of the activity log.
552
553     ``bitMap``
554       Number of updates to the bitmap area of the metadata.
555
556     ``localCount``
557       Number of open requests to the local I/O subsystem.
558
559     ``pending``
560       Number of requests sent to the partner but not yet answered.
561
562     ``unacknowledged``
563       Number of requests received by the partner but still to be answered.
564
565     ``applicationPending``
566       Num of block input/output requests forwarded to DRBD but that have not yet
567       been answered.
568
569     ``epochs``
570       (Optional) Number of epoch objects. Not provided by all DRBD versions.
571
572     ``writeOrder``
573       (Optional) Currently used write ordering method. Not provided by all DRBD
574       versions.
575
576     ``outOfSync``
577       (Optional) KiB of storage currently out of sync. Not provided by all DRBD
578       versions.
579
580   ``syncStatus``
581     (Optional) The status of the synchronization of the disk. This is present
582     only if the disk is being synchronized, and includes the following fields:
583
584     ``percentage``
585       The percentage of synchronized data.
586
587     ``progress``
588       How far the synchronization is. Written as "x/y", where x and y are
589       integer numbers expressed in the measurement unit stated in
590       ``progressUnit``
591
592     ``progressUnit``
593       The measurement unit for the progress indicator.
594
595     ``timeToFinish``
596       The expected time before finishing the synchronization.
597
598     ``speed``
599       The speed of the synchronization.
600
601     ``want``
602       The desiderd speed of the synchronization.
603
604     ``speedUnit``
605       The measurement unit of the ``speed`` and ``want`` values. Expressed
606       as "size/time".
607
608   ``instance``
609     The name of the Ganeti instance this disk is associated to.
610
611
612 Ganeti daemons status
613 +++++++++++++++++++++
614
615 Ganeti will report what information it has about its own daemons.
616 This should allow identifying possible problems with the Ganeti system itself:
617 for example memory leaks, crashes and high resource utilization should be
618 evident by analyzing this information.
619
620 The ``kind`` field will be ``1`` (`Status reporting collectors`_).
621
622 Each daemon will have its own data collector, and each of them will have
623 a ``category`` field valued ``daemon``.
624
625 When executed in verbose mode, their data section will include at least:
626
627 ``memory``
628   The amount of used memory.
629
630 ``size_unit``
631   The measurement unit used for the memory.
632
633 ``uptime``
634   The uptime of the daemon.
635
636 ``CPU usage``
637   How much cpu the daemon is using (percentage).
638
639 Any other daemon-specific information can be included as well in the ``data``
640 section.
641
642 Hypervisor resources report
643 +++++++++++++++++++++++++++
644
645 Each hypervisor has a view of system resources that sometimes is
646 different than the one the OS sees (for example in Xen the Node OS,
647 running as Dom0, has access to only part of those resources). In this
648 section we'll report all information we can in a "non hypervisor
649 specific" way. Each hypervisor can then add extra specific information
650 that is not generic enough be abstracted.
651
652 The ``kind`` field will be ``0`` (`Performance reporting collectors`_).
653
654 Each of the hypervisor data collectory will be of ``category``: ``hypervisor``.
655
656 Node OS resources report
657 ++++++++++++++++++++++++
658
659 Since Ganeti assumes it's running on Linux, it's useful to export some
660 basic information as seen by the host system.
661
662 The ``category`` field of the report will be ``null``.
663
664 The ``kind`` field will be ``0`` (`Performance reporting collectors`_).
665
666 The ``data`` section will include:
667
668 ``cpu_number``
669   The number of available cpus.
670
671 ``cpus``
672   A list with one element per cpu, showing its average load.
673
674 ``memory``
675   The current view of memory (free, used, cached, etc.)
676
677 ``filesystem``
678   A list with one element per filesystem, showing a summary of the
679   total/available space.
680
681 ``NICs``
682   A list with one element per network interface, showing the amount of
683   sent/received data, error rate, IP address of the interface, etc.
684
685 ``versions``
686   A map using the name of a component Ganeti interacts (Linux, drbd,
687   hypervisor, etc) as the key and its version number as the value.
688
689 Note that we won't go into any hardware specific details (e.g. querying a
690 node RAID is outside the scope of this, and can be implemented as a
691 plugin) but we can easily just report the information above, since it's
692 standard enough across all systems.
693
694 Format of the query
695 -------------------
696
697 .. include:: monitoring-query-format.rst
698
699 Instance disk status propagation
700 --------------------------------
701
702 As for the instance status Ganeti has now only partial information about
703 its instance disks: in particular each node is unaware of the disk to
704 instance mapping, that exists only on the master.
705
706 For this design doc we plan to fix this by changing all RPCs that create
707 a backend storage or that put an already existing one in use and passing
708 the relevant instance to the node. The node can then export these to the
709 status reporting tool.
710
711 While we haven't implemented these RPC changes yet, we'll use Confd to
712 fetch this information in the data collectors.
713
714 Plugin system
715 -------------
716
717 The monitoring system will be equipped with a plugin system that can
718 export specific local information through it.
719
720 The plugin system is expected to be used by local installations to
721 export any installation specific information that they want to be
722 monitored, about either hardware or software on their systems.
723
724 The plugin system will be in the form of either scripts or binaries whose output
725 will be inserted in the report.
726
727 Eventually support for other kinds of plugins might be added as well, such as
728 plain text files which will be inserted into the report, or local unix or
729 network sockets from which the information has to be read.  This should allow
730 most flexibility for implementing an efficient system, while being able to keep
731 it as simple as possible.
732
733 Data collectors
734 ---------------
735
736 In order to ease testing as well as to make it simple to reuse this
737 subsystem it will be possible to run just the "data collectors" on each
738 node without passing through the agent daemon.
739
740 If a data collector is run independently, it should print on stdout its
741 report, according to the format corresponding to a single data collector
742 report object, as described in the previous paragraphs.
743
744 Mode of operation
745 -----------------
746
747 In order to be able to report information fast the monitoring agent
748 daemon will keep an in-memory or on-disk cache of the status, which will
749 be returned when queries are made. The status system will then
750 periodically check resources to make sure the status is up to date.
751
752 Different parts of the report will be queried at different speeds. These
753 will depend on:
754 - how often they vary (or we expect them to vary)
755 - how fast they are to query
756 - how important their freshness is
757
758 Of course the last parameter is installation specific, and while we'll
759 try to have defaults, it will be configurable. The first two instead we
760 can use adaptively to query a certain resource faster or slower
761 depending on those two parameters.
762
763 When run as stand-alone binaries, the data collector will not using any
764 caching system, and just fetch and return the data immediately.
765
766 Implementation place
767 --------------------
768
769 The status daemon will be implemented as a standalone Haskell daemon. In
770 the future it should be easy to merge multiple daemons into one with
771 multiple entry points, should we find out it saves resources and doesn't
772 impact functionality.
773
774 The libekg library should be looked at for easily providing metrics in
775 json format.
776
777 Implementation order
778 --------------------
779
780 We will implement the agent system in this order:
781
782 - initial example data collectors (eg. for drbd and instance status).
783 - initial daemon for exporting data, integrating the existing collectors
784 - plugin system
785 - RPC updates for instance status reasons and disk to instance mapping
786 - cache layer for the daemon
787 - more data collectors
788
789
790 Future work
791 ===========
792
793 As a future step it can be useful to "centralize" all this reporting
794 data on a single place. This for example can be just the master node, or
795 all the master candidates. We will evaluate doing this after the first
796 node-local version has been developed and tested.
797
798 Another possible change is replacing the "read-only" RPCs with queries
799 to the agent system, thus having only one way of collecting information
800 from the nodes from a monitoring system and for Ganeti itself.
801
802 One extra feature we may need is a way to query for only sub-parts of
803 the report (eg. instances status only). This can be done by passing
804 arguments to the HTTP GET, which will be defined when we get to this
805 funtionality.
806
807 Finally the :doc:`autorepair system design <design-autorepair>`. system
808 (see its design) can be expanded to use the monitoring agent system as a
809 source of information to decide which repairs it can perform.
810
811 .. vim: set textwidth=72 :
812 .. Local Variables:
813 .. mode: rst
814 .. fill-column: 72
815 .. End: