commit 6c2d0b4436b7155a748e26372e612272a65b164e
Author: Iustin Pop <iustin@google.com>
Date:   Mon Nov 17 11:30:42 2008 +0000

    More updates to the documentation
    
    This patch removes the client-api.txt document (since it's obsoleted by
    the documentation inside the design-2.0.rst document) and adds many
    updates to the latter.
    
    Reviewed-by: imsnah

diff --git a/doc/design-2.0.rst b/doc/design-2.0.rst
index 4863d9e..9b40e75 100644
--- a/doc/design-2.0.rst
+++ b/doc/design-2.0.rst
@@ -22,7 +22,7 @@ then the restrictions.
 Background
 ==========
 
-While Ganeti 1.2 is usable, it severly limits the flexibility of the
+While Ganeti 1.2 is usable, it severely limits the flexibility of the
 cluster administration and imposes a very rigid model. It has the
 following main scalability issues:
 
@@ -33,13 +33,19 @@ following main scalability issues:
 It also has a number of artificial restrictions, due to historical design:
 
 - fixed number of disks (two) per instance
-- fixed number of nics
+- fixed number of NICs
 
 .. [#] Replace disks will release the lock, but this is an exception
        and not a recommended way to operate
 
 The 2.0 version is intended to address some of these problems, and
-create a more flexible codebase for future developments.
+create a more flexible code base for future developments.
+
+Among these problems, the single-operation at a time restriction is
+biggest issue with the current version of Ganeti. It is such a big
+impediment in operating bigger clusters that many times one is tempted
+to remove the lock just to do a simple operation like start instance
+while an OS installation is running.
 
 Scalability problems
 --------------------
@@ -60,10 +66,10 @@ lock, without a real reason for that to happen.
 
 One of the main causes of this global lock (beside the higher
 difficulty of ensuring data consistency in a more granular lock model)
-is the fact that currently there is no "master" daemon in Ganeti. Each
-command tries to acquire the so called *cmd* lock and when it
-succeeds, it takes complete ownership of the cluster configuration and
-state.
+is the fact that currently there is no long-lived process in Ganeti
+that can coordinate multiple operations. Each command tries to acquire
+the so called *cmd* lock and when it succeeds, it takes complete
+ownership of the cluster configuration and state.
 
 Other scalability problems are due the design of the DRBD device
 model, which assumed at its creation a low (one to four) number of
@@ -77,6 +83,14 @@ instance model. This is a purely artificial restrictions, but it
 touches multiple areas (configuration, import/export, command line)
 that it's more fitted to a major release than a minor one.
 
+Architecture issues
+-------------------
+
+The fact that each command is a separate process that reads the
+cluster state, executes the command, and saves the new state is also
+an issue on big clusters where the configuration data for the cluster
+begins to be non-trivial in size.
+
 Overview
 ========
 
@@ -109,24 +123,23 @@ Core changes
 
 The main changes will be switching from a per-process model to a
 daemon based model, where the individual gnt-* commands will be
-clients that talk to this daemon (see the design-2.0-master-daemon
-document). This will allow us to get rid of the global cluster lock
-for most operations, having instead a per-object lock (see
-design-2.0-granular-locking). Also, the daemon will be able to queue
-jobs, and this will allow the invidual clients to submit jobs without
-waiting for them to finish, and also see the result of old requests
-(see design-2.0-job-queue).
+clients that talk to this daemon (see `Master daemon`_). This will
+allow us to get rid of the global cluster lock for most operations,
+having instead a per-object lock (see `Granular locking`_). Also, the
+daemon will be able to queue jobs, and this will allow the individual
+clients to submit jobs without waiting for them to finish, and also
+see the result of old requests (see `Job Queue`_).
 
 Beside these major changes, another 'core' change but that will not be
 as visible to the users will be changing the model of object attribute
-storage, and separate that into namespaces (such that an Xen PVM
+storage, and separate that into name spaces (such that an Xen PVM
 instance will not have the Xen HVM parameters). This will allow future
-flexibility in defining additional parameters. More details in the
-design-2.0-cluster-parameters document.
+flexibility in defining additional parameters. For more details see
+`Object parameters`_.
 
 The various changes brought in by the master daemon model and the
 read-write RAPI will require changes to the cluster security; we move
-away from Twisted and use http(s) for intra- and extra-cluster
+away from Twisted and use HTTP(s) for intra- and extra-cluster
 communications. For more details, see the security document in the
 doc/ directory.
 
@@ -140,36 +153,57 @@ In Ganeti 2.0, we will have the following *entities*:
 - the command line tools (on the master node)
 - the RAPI daemon (on the master node)
 
-Interaction paths are between:
+The master-daemon related interaction paths are:
 
-- (CLI tools/RAPI daemon) and the master daemon, via the so called *luxi* API
+- (CLI tools/RAPI daemon) and the master daemon, via the so called *LUXI* API
 - the master daemon and the node daemons, via the node RPC
 
+There are also some additional interaction paths for exceptional cases:
+
+- CLI tools might access via SSH the nodes (for ``gnt-cluster copyfile``
+  and ``gnt-cluster command``)
+- master failover is a special case when a non-master node will SSH
+  and do node-RPC calls to the current master
+
 The protocol between the master daemon and the node daemons will be
-changed to HTTP(S), using a simple PUT/GET of JSON-encoded
-messages. This is done due to difficulties in working with the Twisted
-framework and its protocols in a multithreaded environment, which we
-can overcome by using a simpler stack (see the caveats section). The
-protocol between the CLI/RAPI and the master daemon will be a custom
-one (called *luxi*): on a UNIX socket on the master node, with rights
-restricted by filesystem permissions, the CLI/RAPI will talk to the
-master daemon using JSON-encoded messages.
+changed from (Ganeti 1.2) Twisted PB (perspective broker) to HTTP(S),
+using a simple PUT/GET of JSON-encoded messages. This is done due to
+difficulties in working with the Twisted framework and its protocols
+in a multithreaded environment, which we can overcome by using a
+simpler stack (see the caveats section).
+
+The protocol between the CLI/RAPI and the master daemon will be a
+custom one (called *LUXI*): on a UNIX socket on the master node, with
+rights restricted by filesystem permissions, the CLI/RAPI will talk to
+the master daemon using JSON-encoded messages.
 
 The operations supported over this internal protocol will be encoded
 via a python library that will expose a simple API for its
 users. Internally, the protocol will simply encode all objects in JSON
 format and decode them on the receiver side.
 
+For more details about the RAPI daemon see `Remote API changes`_, and
+for the node daemon see `Node daemon changes`_.
+
 The LUXI protocol
 +++++++++++++++++
 
-We will have two main classes of operations over the master daemon API:
+As described above, the protocol for making requests or queries to the
+master daemon will be a UNIX-socket based simple RPC of JSON-encoded
+messages.
+
+The choice of UNIX was in order to get rid of the need of
+authentication and authorisation inside Ganeti; for 2.0, the
+permissions on the Unix socket itself will determine the access
+rights.
+
+We will have two main classes of operations over this API:
 
 - cluster query functions
 - job related functions
 
 The cluster query functions are usually short-duration, and are the
-equivalent of the OP_QUERY_* opcodes in ganeti 1.2 (and they are
+equivalent of the ``OP_QUERY_*`` opcodes in Ganeti 1.2 (and they are
 internally implemented still with these opcodes). The clients are
 guaranteed to receive the response in a reasonable time via a timeout.
 
@@ -180,10 +214,45 @@ The job-related functions will be:
 - archive job (see the job queue design doc)
 - wait for job change, which allows a client to wait without polling
 
-For more details, see the job queue design document.
+For more details of the actual operation list, see the `Job Queue`_.
 
-Daemon implementation
-+++++++++++++++++++++
+Both requests and responses will consist of a JSON-encoded message
+followed by the ``ETX`` character (ASCII decimal 3), which is not a
+valid character in JSON messages and thus can serve as a message
+delimiter. The contents of the messages will be a dictionary with two
+fields:
+
+:method:
+  the name of the method called
+:args:
+  the arguments to the method, as a list (no keyword arguments allowed)
+
+Responses will follow the same format, with the two fields being:
+
+:success:
+  a boolean denoting the success of the operation
+:result:
+  the actual result, or error message in case of failure
+
+There are two special value for the result field:
+
+- in the case that the operation failed, and this field is a list of
+  length two, the client library will try to interpret is as an exception,
+  the first element being the exception type and the second one the
+  actual exception arguments; this will allow a simple method of passing
+  Ganeti-related exception across the interface
+- for the *WaitForChange* call (that waits on the server for a job to
+  change status), if the result is equal to ``nochange`` instead of the
+  usual result for this call (a list of changes), then the library will
+  internally retry the call; this is done in order to differentiate
+  internally between master daemon hung and job simply not changed
+
+Users of the API that don't use the provided python library should
+take care of the above two cases.
+
+
+Master daemon implementation
+++++++++++++++++++++++++++++
 
 The daemon will be based around a main I/O thread that will wait for
 new requests from the clients, and that does the setup/shutdown of the
@@ -195,7 +264,7 @@ There will two other classes of threads in the daemon:
   long-lived, started at daemon startup and terminated only at shutdown
   time
 - client I/O threads, which are the ones that talk the local protocol
-  to the clients
+  (LUXI) to the clients, and are short-lived
 
 Master startup/failover
 +++++++++++++++++++++++
@@ -229,19 +298,30 @@ failover:
     - if we are not failing over (but just starting), the
       quorum agrees that we are the designated master
 
+    - if any of the above is false, we prevent the current operation
+      (i.e. we don't become the master)
+
 #. at this point, the node transitions to the master role
 
 #. for all the in-progress jobs, mark them as failed, with
    reason unknown or something similar (master failed, etc.)
 
+Since due to exceptional conditions we could have a situation in which
+no node can become the master due to inconsistent data, we will have
+an override switch for the master daemon startup that will assume the
+current node has the right data and will replicate all the
+configuration files to the other nodes.
+
+**Note**: the above algorithm is by no means an election algorithm; it
+is a *confirmation* of the master role currently held by a node.
 
 Logging
 +++++++
 
-The logging system will be switched completely to the logging module;
-currently it's logging-based, but exposes a different API, which is
-just overhead. As such, the code will be switched over to standard
-logging calls, and only the setup will be custom.
+The logging system will be switched completely to the standard python
+logging module; currently it's logging-based, but exposes a different
+API, which is just overhead. As such, the code will be switched over
+to standard logging calls, and only the setup will be custom.
 
 With this change, we will remove the separate debug/info/error logs,
 and instead have always one logfile per daemon model:
@@ -250,11 +330,22 @@ and instead have always one logfile per daemon model:
 - node-daemon.log for the node daemon (this is the same as in 1.2)
 - rapi-daemon.log for the RAPI daemon logs
 - rapi-access.log, an additional log file for the RAPI that will be
-  in the standard http log format for possible parsing by other tools
+  in the standard HTTP log format for possible parsing by other tools
+
+Since the `watcher`_ will only submit jobs to the master for startup
+of the instances, its log file will contain less information than
+before, mainly that it will start the instance, but not the results.
+
+Node daemon changes
++++++++++++++++++++
+
+The only change to the node daemon is that, since we need better
+concurrency, we don't process the inter-node RPC calls in the node
+daemon itself, but we fork and process each request in a separate
+child.
 
-Since the watcher will only submit jobs to the master for startup of
-the instances, its log file will contain less information than before,
-mainly that it will start the instance, but not the results.
+Since we don't have many calls, and we only fork (not exec), the
+overhead should be minimal.
 
 Caveats
 +++++++
@@ -277,20 +368,33 @@ chosen this approach is:
   much better served by a daemon-based model
 
 Another area of discussion is moving away from Twisted in this new
-implementation. While Twisted hase its advantages, there are also many
-disatvantanges to using it:
+implementation. While Twisted has its advantages, there are also many
+disadvantages to using it:
 
 - first and foremost, it's not a library, but a framework; thus, if
-  you use twisted, all the code needs to be 'twiste-ized'; we were able
-  to keep the 1.x code clean by hacking around twisted in an
-  unsupported, unrecommended way, and the only alternative would have
-  been to make all the code be written for twisted
-- it has some weaknesses in working with multiple threads, since its base
-  model is designed to replace thread usage by using deferred calls, so while
-  it can use threads, it's not less flexible in doing so
-
-And, since we already have an HTTP server library for the RAPI, we
-can just reuse that for inter-node communication.
+  you use twisted, all the code needs to be 'twiste-ized' and written
+  in an asynchronous manner, using deferreds; while this method works,
+  it's not a common way to code and it requires that the entire process
+  workflow is based around a single *reactor* (Twisted name for a main
+  loop)
+- the more advanced granular locking that we want to implement would
+  require, if written in the async-manner, deep integration with the
+  Twisted stack, to such an extend that business-logic is inseparable
+  from the protocol coding; we felt that this is an unreasonable request,
+  and that a good protocol library should allow complete separation of
+  low-level protocol calls and business logic; by comparison, the threaded
+  approach combined with HTTPs protocol required (for the first iteration)
+  absolutely no changes from the 1.2 code, and later changes for optimizing
+  the inter-node RPC calls required just syntactic changes (e.g.
+  ``rpc.call_...`` to ``self.rpc.call_...``)
+
+Another issue is with the Twisted API stability - during the Ganeti
+1.x lifetime, we had to to implement many times workarounds to changes
+in the Twisted version, so that for example 1.2 is able to use both
+Twisted 2.x and 8.x.
+
+In the end, since we already had an HTTP server library for the RAPI,
+we just reused that for inter-node communication.
 
 
 Granular locking
@@ -302,48 +406,49 @@ operations don't step on each other toes and break the cluster.
 
 This design addresses how we are going to deal with locking so that:
 
-- high urgency operations are not stopped by long length ones
-- long length operations can run in parallel
-- we preserve safety (data coherency) and liveness (no deadlock, no work
-  postponed indefinitely) on the cluster
+- we preserve data coherency
+- we prevent deadlocks
+- we prevent job starvation
 
 Reaching the maximum possible parallelism is a Non-Goal. We have identified a
 set of operations that are currently bottlenecks and need to be parallelised
 and have worked on those. In the future it will be possible to address other
 needs, thus making the cluster more and more parallel one step at a time.
 
-This document only talks about parallelising Ganeti level operations, aka
-Logical Units, and the locking needed for that. Any other synchronisation lock
+This section only talks about parallelising Ganeti level operations, aka
+Logical Units, and the locking needed for that. Any other synchronization lock
 needed internally by the code is outside its scope.
 
-Ganeti 1.2
-++++++++++
-
-We intend to implement a Ganeti locking library, which can be used by the
-various ganeti code components in order to easily, efficiently and correctly
-grab the locks they need to perform their function.
+Library details
++++++++++++++++
 
 The proposed library has these features:
 
-- Internally managing all the locks, making the implementation transparent
+- internally managing all the locks, making the implementation transparent
   from their usage
-- Automatically grabbing multiple locks in the right order (avoid deadlock)
-- Ability to transparently handle conversion to more granularity
-- Support asynchronous operation (future goal)
-
-Locking will be valid only on the master node and will not be a distributed
-operation. In case of master failure, though, if some locks were held it means
-some opcodes were in progress, so when recovery of the job queue is done it
-will be possible to determine by the interrupted opcodes which operations could
-have been left half way through and thus which locks could have been held. It
-is then the responsibility either of the master failover code, of the cluster
-verification code, or of the admin to do what's necessary to make sure that any
-leftover state is dealt with. This is not an issue from a locking point of view
-because the fact that the previous master has failed means that it cannot do
-any job.
-
-A corollary of this is that a master-failover operation with both masters alive
-needs to happen while no other locks are held.
+- automatically grabbing multiple locks in the right order (avoid deadlock)
+- ability to transparently handle conversion to more granularity
+- support asynchronous operation (future goal)
+
+Locking will be valid only on the master node and will not be a
+distributed operation. Therefore, in case of master failure, the
+operations currently running will be aborted and the locks will be
+lost; it remains to the administrator to cleanup (if needed) the
+operation result (e.g. make sure an instance is either installed
+correctly or removed).
+
+A corollary of this is that a master-failover operation with both
+masters alive needs to happen while no operations are running, and
+therefore no locks are held.
+
+All the locks will be represented by objects (like
+``lockings.SharedLock``), and the individual locks for each object
+will be created at initialisation time, from the config file.
+
+The API will have a way to grab one or more than one locks at the same time.
+Any attempt to grab a lock while already holding one in the wrong order will be
+checked for, and fail.
+
 
 The Locks
 +++++++++
@@ -360,12 +465,18 @@ time for multiple instances and nodes, and internal ordering will be dealt
 within the locking library, which, for simplicity, will just use alphabetical
 order.
 
+Each lock has the following three possible statuses:
+
+- unlocked (anyone can grab the lock)
+- shared (anyone can grab/have the lock but only in shared mode)
+- exclusive (no one else can grab/have the lock)
+
 Handling conversion to more granularity
 +++++++++++++++++++++++++++++++++++++++
 
 In order to convert to a more granular approach transparently each time we
 split a lock into more we'll create a "metalock", which will depend on those
-sublocks and live for the time necessary for all the code to convert (or
+sub-locks and live for the time necessary for all the code to convert (or
 forever, in some conditions). When a metalock exists all converted code must
 acquire it in shared mode, so it can run concurrently, but still be exclusive
 with old code, which acquires it exclusively.
@@ -373,7 +484,7 @@ with old code, which acquires it exclusively.
 In the beginning the only such lock will be what replaces the current "command"
 lock, and will acquire all the locks in the system, before proceeding. This
 lock will be called the "Big Ganeti Lock" because holding that one will avoid
-any other concurrent ganeti operations.
+any other concurrent Ganeti operations.
 
 We might also want to devise more metalocks (eg. all nodes, all nodes+config)
 in order to make it easier for some parts of the code to acquire what it needs
@@ -383,16 +494,6 @@ In the future things like the node locks could become metalocks, should we
 decide to split them into an even more fine grained approach, but this will
 probably be only after the first 2.0 version has been released.
 
-Library API
-+++++++++++
-
-All the locking will be its own class, and the locks will be created at
-initialisation time, from the config file.
-
-The API will have a way to grab one or more than one locks at the same time.
-Any attempt to grab a lock while already holding one in the wrong order will be
-checked for, and fail.
-
 Adding/Removing locks
 +++++++++++++++++++++
 
@@ -405,11 +506,11 @@ metalocks that guarantee to grab sets of resources without specifying them
 explicitly. The implementation of this will be handled in the locking library
 itself.
 
-Of course when instances or nodes disappear from the cluster the relevant locks
-must be removed. This is easier than adding new elements, as the code which
-removes them must own them exclusively or can queue for their ownership, and
-thus deals with metalocks exactly as normal code acquiring those locks. Any
-operation queueing on a removed lock will fail after its removal.
+When instances or nodes disappear from the cluster the relevant locks
+must be removed. This is easier than adding new elements, as the code
+which removes them must own them exclusively already, and thus deals
+with metalocks exactly as normal code acquiring those locks. Any
+operation queuing on a removed lock will fail after its removal.
 
 Asynchronous operations
 +++++++++++++++++++++++
@@ -421,8 +522,8 @@ the request is impossible or somehow erroneous.
 In the future we may want to implement different types of asynchronous
 operations such as:
 
-- Try to acquire this lock set and fail if not possible
-- Try to acquire one of these lock sets and return the first one you were
+- try to acquire this lock set and fail if not possible
+- try to acquire one of these lock sets and return the first one you were
   able to get (or after a timeout) (select/poll like)
 
 These operations can be used to prioritize operations based on available locks,
@@ -441,8 +542,8 @@ level.  In the future we may want to split logical units in independent
 "tasklets" with their own locking requirements. A different design doc (or mini
 design doc) will cover the move from Logical Units to tasklets.
 
-Lock acquisition code path
-++++++++++++++++++++++++++
+Code examples
++++++++++++++
 
 In general when acquiring locks we should use a code path equivalent to::
 
@@ -453,9 +554,11 @@ In general when acquiring locks we should use a code path equivalent to::
   finally:
     lock.release()
 
-This makes sure we release all locks, and avoid possible deadlocks. Of course
-extra care must be used not to leave, if possible locked structures in an
-unusable state.
+This makes sure we release all locks, and avoid possible deadlocks. Of
+course extra care must be used not to leave, if possible locked
+structures in an unusable state. Note that with Python 2.5 a simpler
+syntax will be possible, but we want to keep compatibility with Python
+2.4 so the new constructs should not be used.
 
 In order to avoid this extra indentation and code changes everywhere in the
 Logical Units code, we decided to allow LUs to declare locks, and then execute
@@ -500,7 +603,7 @@ Granular locking is not enough to speed up operations, we also need a
 queue to store these and to be able to process as many as possible in
 parallel.
 
-A ganeti job will consist of multiple ``OpCodes`` which are the basic
+A Ganeti job will consist of multiple ``OpCodes`` which are the basic
 element of operation in Ganeti 1.2 (and will remain as such). Most
 command-level commands are equivalent to one OpCode, or in some cases
 to a sequence of opcodes, all of the same type (e.g. evacuating a node
@@ -518,7 +621,7 @@ Job execution—“Life of a Ganeti job”
    of the waiting threads will pick up the new job.
 #. Client waits for job status updates by calling a waiting RPC function.
    Log message may be shown to the user. Until the job is started, it can also
-   be cancelled.
+   be canceled.
 #. As soon as the job is finished, its final result and status can be retrieved
    from the server.
 #. If the client archives the job, it gets moved to a history directory.
@@ -653,10 +756,10 @@ History
 +++++++
 
 Archived jobs are kept in a separate directory,
-/var/lib/ganeti/queue/archive/.  This is done in order to speed up the
-queue handling: by default, the jobs in the archive are not touched by
-any functions. Only the current (unarchived) jobs are parsed, loaded,
-and verified (if implemented) by the master daemon.
+``/var/lib/ganeti/queue/archive/``.  This is done in order to speed up
+the queue handling: by default, the jobs in the archive are not
+touched by any functions. Only the current (unarchived) jobs are
+parsed, loaded, and verified (if implemented) by the master daemon.
 
 
 Ganeti updates
@@ -667,7 +770,6 @@ in the job queue structure. In order to allow this, there will be a
 way to prevent new jobs entering the queue.
 
 
-
 Object parameters
 ~~~~~~~~~~~~~~~~~
 
@@ -697,7 +799,7 @@ The following definitions for instance parameters will be used below:
   a hypervisor parameter (or hypervisor specific parameter) is defined
   as a parameter that is interpreted by the hypervisor support code in
   Ganeti and usually is specific to a particular hypervisor (like the
-  kernel path for PVM which makes no sense for HVM).
+  kernel path for `PVM`_ which makes no sense for `HVM`_).
 
 :backend parameter:
   a backend parameter is defined as an instance parameter that can be
@@ -727,7 +829,7 @@ Cluster object. In addition, two new attributes at this level will
 hold defaults for the instances:
 
 - hvparams, a dictionary indexed by hypervisor type, holding default
-  values for hypervisor parameters that are not defined/overrided by
+  values for hypervisor parameters that are not defined/overridden by
   the instances of this hypervisor type
 
 - beparams, a dictionary holding (for 2.0) a single element 'default',
@@ -754,12 +856,12 @@ until reset).
 The names for hypervisor parameters in the instance.hvparams subtree
 should be choosen as generic as possible, especially if specific
 parameters could conceivably be useful for more than one hypervisor,
-e.g. instance.hvparams.vnc_console_port instead of using both
-instance.hvparams.hvm_vnc_console_port and
-instance.hvparams.kvm_vnc_console_port.
+e.g. ``instance.hvparams.vnc_console_port`` instead of using both
+``instance.hvparams.hvm_vnc_console_port`` and
+``instance.hvparams.kvm_vnc_console_port``.
 
 There are some special cases related to disks and NICs (for example):
-a disk has both ganeti-related parameters (e.g. the name of the LV)
+a disk has both Ganeti-related parameters (e.g. the name of the LV)
 and hypervisor-related parameters (how the disk is presented to/named
 in the instance). The former parameters remain as proper-instance
 parameters, while the latter value are migrated to the hvparams
@@ -806,9 +908,9 @@ following features:
   for this hypervisor
 :CheckParamSyntax(hvparams): checks that the given parameters are
   valid (as in the names are valid) for this hypervisor; usually just
-  comparing hvparams.keys() and cls.PARAMETERS; this is a class method
-  that can be called from within master code (i.e. cmdlib) and should
-  be safe to do so
+  comparing ``hvparams.keys()`` and ``cls.PARAMETERS``; this is a class
+  method that can be called from within master code (i.e. cmdlib) and
+  should be safe to do so
 :ValidateParameters(hvparams): verifies the values of the provided
   parameters against this hypervisor; this is a method that will be
   called on the target node, from backend.py code, and as such can
@@ -839,15 +941,15 @@ Opcode changes
 The parameter changes will have impact on the OpCodes, especially on
 the following ones:
 
-- OpCreateInstance, where the new hv and be parameters will be sent as
+- ``OpCreateInstance``, where the new hv and be parameters will be sent as
   dictionaries; note that all hv and be parameters are now optional, as
   the values can be instead taken from the cluster
-- OpQueryInstances, where we have to be able to query these new
+- ``OpQueryInstances``, where we have to be able to query these new
   parameters; the syntax for names will be ``hvparam/$NAME`` and
   ``beparam/$NAME`` for querying an individual parameter out of one
   dictionary, and ``hvparams``, respectively ``beparams``, for the whole
   dictionaries
-- OpModifyInstance, where the the modified parameters are sent as
+- ``OpModifyInstance``, where the the modified parameters are sent as
   dictionaries
 
 Additionally, we will need new OpCodes to modify the cluster-level
@@ -891,11 +993,11 @@ estimated usage patters. However, experience has later shown that some
 assumptions made initially are not true and that more flexibility is
 needed.
 
-One main assupmtion made was that disk failures should be treated as 'rare'
+One main assumption made was that disk failures should be treated as 'rare'
 events, and that each of them needs to be manually handled in order to ensure
 data safety; however, both these assumptions are false:
 
-- disk failures can be a common occurence, based on usage patterns or cluster
+- disk failures can be a common occurrence, based on usage patterns or cluster
   size
 - our disk setup is robust enough (referring to DRBD8 + LVM) that we could
   automate more of the recovery
@@ -956,30 +1058,30 @@ possible, otherwise we abort) and start it with our own
 parameters.
 
 This means that we in effect take ownership of the minor space for
-that device type; if there's a user-created drbd minor, it will be
+that device type; if there's a user-created DRBD minor, it will be
 automatically removed.
 
 The change will have the effect of reducing the number of external
 commands run per device from a constant number times the index of the
 first free DRBD minor to just a constant number.
 
-Removal of obsolete device types (md, drbd7)
+Removal of obsolete device types (MD, DRBD7)
 ++++++++++++++++++++++++++++++++++++++++++++
 
 We need to remove these device types because of two issues. First,
-drbd7 has bad failure modes in case of dual failures (both network and
+DRBD7 has bad failure modes in case of dual failures (both network and
 disk - it cannot propagate the error up the device stack and instead
-just panics. Second, due to the assymetry between primary and
-secondary in md+drbd mode, we cannot do live failover (not even if we
-had md+drbd8).
+just panics. Second, due to the asymmetry between primary and
+secondary in MD+DRBD mode, we cannot do live failover (not even if we
+had MD+DRBD8).
 
 File-based storage support
 ++++++++++++++++++++++++++
 
-This is covered by a separate design doc (<em>Vinales</em>) and
-would allow us to get rid of the hard requirement for testing
-clusters; it would also allow people who have SAN storage to do live
-failover taking advantage of their storage solution.
+Using files instead of logical volumes for instance storage would
+allow us to get rid of the hard requirement for volume groups for
+testing clusters and it would also allow usage of SAN storage to do
+live failover taking advantage of this storage solution.
 
 Better LVM allocation
 +++++++++++++++++++++
@@ -1030,9 +1132,9 @@ method:
 #. if no, and previous status was no, do nothing
 #. if no, and previous status was yes:
     #. if more than one node is inconsistent, do nothing
-    #. if only one node is incosistent:
+    #. if only one node is inconsistent:
         #. run ``vgreduce --removemissing``
-        #. log this occurence in the ganeti log in a form that
+        #. log this occurrence in the Ganeti log in a form that
            can be used for monitoring
         #. [FUTURE] run ``replace-disks`` for all
            instances affected
@@ -1067,9 +1169,9 @@ The algorithm for performing the failover is straightforward:
 - verify that S2 (the node the user has chosen to keep as secondary) has
   valid data (is consistent)
 
-- tear down the current DRBD association and setup a drbd pairing between
+- tear down the current DRBD association and setup a DRBD pairing between
   P2 (P2 is indicated by the user) and S2; since P2 has no data, it will
-  start resyncing from S2
+  start re-syncing from S2
 
 - as soon as P2 is in state SyncTarget (i.e. after the resync has started
   but before it has finished), we can promote it to primary role (r/w)
@@ -1083,7 +1185,7 @@ Caveats: during the P2?S2 sync, a (non-transient) network error
 will cause I/O errors on the instance, so (if a longer instance
 downtime is acceptable) we can postpone the restart of the instance
 until the resync is done. However, disk I/O errors on S2 will cause
-dataloss, since we don't have a good copy of the data anymore, so in
+data loss, since we don't have a good copy of the data anymore, so in
 this case waiting for the sync to complete is not an option. As such,
 it is recommended that this feature is used only in conjunction with
 proper disk monitoring.
@@ -1096,14 +1198,14 @@ Caveats
 +++++++
 
 The dynamic device model, while more complex, has an advantage: it
-will not reuse by mistake another's instance DRBD device, since it
-always looks for either our own or a free one.
+will not reuse by mistake the DRBD device of another instance, since
+it always looks for either our own or a free one.
 
 The static one, in contrast, will assume that given a minor number N,
 it's ours and we can take over. This needs careful implementation such
 that if the minor is in use, either we are able to cleanly shut it
 down, or we abort the startup. Otherwise, it could be that we start
-syncing between two instance's disks, causing dataloss.
+syncing between two instance's disks, causing data loss.
 
 
 Variable number of disk/NICs per instance
@@ -1115,7 +1217,7 @@ Variable number of disks
 In order to support high-security scenarios (for example read-only sda
 and read-write sdb), we need to make a fully flexibly disk
 definition. This has less impact that it might look at first sight:
-only the instance creation has hardcoded number of disks, not the disk
+only the instance creation has hard coded number of disks, not the disk
 handling code. The block device handling and most of the instance
 handling code is already working with "the instance's disks" as
 opposed to "the two disks of the instance", but some pieces are not
@@ -1123,7 +1225,7 @@ opposed to "the two disks of the instance", but some pieces are not
 
 The objective is to be able to specify the number of disks at
 instance creation, and to be able to toggle from read-only to
-read-write a disk afterwards.
+read-write a disk afterward.
 
 Variable number of NICs
 +++++++++++++++++++++++
@@ -1131,7 +1233,7 @@ Variable number of NICs
 Similar to the disk change, we need to allow multiple network
 interfaces per instance. This will affect the internal code (some
 function will have to stop assuming that ``instance.nics`` is a list
-of length one), the OS api which currently can export/import only one
+of length one), the OS API which currently can export/import only one
 instance, and the command line interface.
 
 Interface changes
@@ -1176,7 +1278,7 @@ what can be assumed and what cannot be regarding Ganeti environment.
 When designing the new OS API our priorities are:
 - ease of use
 - future extensibility
-- ease of porting from the old api
+- ease of porting from the old API
 - modularity
 
 As such we want to limit the number of scripts that must be written to support
@@ -1228,7 +1330,7 @@ the ones for 2.0:
   instances will be forced to have a number of disks greater or equal to the
   one of the export.
 - Some scripts are not compulsory: if such a script is missing the relevant
-  operations will be forbidden for instances of that os. This makes it easier
+  operations will be forbidden for instances of that OS. This makes it easier
   to distinguish between unsupported operations and no-op ones (if any).
 
 
@@ -1239,18 +1341,18 @@ Rather than using command line flags, as they do now, scripts will accept
 inputs from environment variables.  We expect the following input values:
 
 OS_API_VERSION
-  The version of the OS api that the following parameters comply with;
+  The version of the OS API that the following parameters comply with;
   this is used so that in the future we could have OSes supporting
   multiple versions and thus Ganeti send the proper version in this
   parameter
 INSTANCE_NAME
   Name of the instance acted on
 HYPERVISOR
-  The hypervisor the instance should run on (eg. 'xen-pvm', 'xen-hvm', 'kvm')
+  The hypervisor the instance should run on (e.g. 'xen-pvm', 'xen-hvm', 'kvm')
 DISK_COUNT
   The number of disks this instance will have
 NIC_COUNT
-  The number of nics this instance will have
+  The number of NICs this instance will have
 DISK_<N>_PATH
   Path to the Nth disk.
 DISK_<N>_ACCESS
@@ -1268,14 +1370,16 @@ NIC_<N>_IP
 NIC_<N>_BRIDGE
   Node bridge the Nth network interface will be connected to
 NIC_<N>_FRONTEND_TYPE
-  Type of the Nth nic as seen by the instance. For example 'virtio', 'rtl8139', etc.
+  Type of the Nth NIC as seen by the instance. For example 'virtio',
+  'rtl8139', etc.
 DEBUG_LEVEL
   Whether more out should be produced, for debugging purposes. Currently the
   only valid values are 0 and 1.
 
-These are only the basic variables we are thinking of now, but more may come
-during the implementation and they will be documented in the ganeti-os-api man
-page. All these variables will be available to all scripts.
+These are only the basic variables we are thinking of now, but more
+may come during the implementation and they will be documented in the
+``ganeti-os-api`` man page. All these variables will be available to
+all scripts.
 
 Some scripts will need a few more information to work. These will have
 per-script variables, such as for example:
@@ -1304,7 +1408,7 @@ As discussed scripts should only send user-targeted information to stderr. The
 create and import scripts are supposed to format/initialise the given block
 devices and install the correct instance data. The export script is supposed to
 export instance data to stdout in a format understandable by the the import
-script. The data will be compressed by ganeti, so no compression should be
+script. The data will be compressed by Ganeti, so no compression should be
 done. The rename script should only modify the instance's knowledge of what
 its name is.
 
@@ -1312,15 +1416,16 @@ Other declarative style features
 ++++++++++++++++++++++++++++++++
 
 Similar to Ganeti 1.2, OS specifications will need to provide a
-'ganeti_api_version' containing list of numbers matching the version(s) of the
-api they implement. Ganeti itself will always be compatible with one version of
-the API and may maintain retrocompatibility if it's feasible to do so. The
-numbers are one-per-line, so an OS supporting both version 5 and version 20
-will have a file containing two lines. This is different from Ganeti 1.2, which
-only supported one version number.
+'ganeti_api_version' containing list of numbers matching the
+version(s) of the API they implement. Ganeti itself will always be
+compatible with one version of the API and may maintain backwards
+compatibility if it's feasible to do so. The numbers are one-per-line,
+so an OS supporting both version 5 and version 20 will have a file
+containing two lines. This is different from Ganeti 1.2, which only
+supported one version number.
 
 In addition to that an OS will be able to declare that it does support only a
-subset of the ganeti hypervisors, by declaring them in the 'hypervisors' file.
+subset of the Ganeti hypervisors, by declaring them in the 'hypervisors' file.
 
 
 Caveats/Notes
@@ -1341,90 +1446,120 @@ file where they are encoded in some format.
 Remote API changes
 ~~~~~~~~~~~~~~~~~~
 
-The first Ganeti RAPI was designed and deployed with the Ganeti 1.2.5 release.
-That version provide Read-Only access to a cluster state. Fully functional
-read-write API demand significant internal changes which are in a pipeline for
-Ganeti 2.0 release.
+The first Ganeti remote API (RAPI) was designed and deployed with the
+Ganeti 1.2.5 release.  That version provide read-only access to the
+cluster state. Fully functional read-write API demands significant
+internal changes which will be implemented in version 2.0.
 
-We decided to go with implementing the Ganeti RAPI in a RESTful way, which is
-aligned with key features we looking. It is simple, stateless, scalable and
-extensible paradigm of API implementation. As transport it uses HTTP over SSL,
-and we are implementing it in JSON encoding, but in a way it possible to extend
-and provide any other one.
+We decided to go with implementing the Ganeti RAPI in a RESTful way,
+which is aligned with key features we looking. It is simple,
+stateless, scalable and extensible paradigm of API implementation. As
+transport it uses HTTP over SSL, and we are implementing it with JSON
+encoding, but in a way it possible to extend and provide any other
+one.
 
 Design
 ++++++
 
-The Ganeti API implemented as independent daemon, running on the same node
-with the same permission level as Ganeti master daemon. Communication done
-through unix socket protocol provided by Ganeti luxi library.
-In order to keep communication asynchronous RAPI process two types of client
-requests:
+The Ganeti RAPI is implemented as independent daemon, running on the
+same node with the same permission level as Ganeti master
+daemon. Communication is done through the LUXI library to the master
+daemon. In order to keep communication asynchronous RAPI processes two
+types of client requests:
 
-- queries: sever able to answer immediately
-- jobs: some time needed.
+- queries: server is able to answer immediately
+- job submission: some time is required for a useful response
 
-In the query case requested data send back to client in http body. Typical
-examples of queries would be list of nodes, instances, cluster info, etc.
-Dealing with jobs client instead of waiting until job completes receive a job
-id, the identifier which allows to query the job progress in the job queue.
-(See job queue design doc for details)
+In the query case requested data send back to client in the HTTP
+response body. Typical examples of queries would be: list of nodes,
+instances, cluster info, etc.
 
-Internally, each exported object has an version identifier, which is used as a
-state stamp in the http header E-Tag field for request/response to avoid a race
-condition.
+In the case of job submission, the client receive a job ID, the
+identifier which allows to query the job progress in the job queue
+(see `Job Queue`_).
+
+Internally, each exported object has an version identifier, which is
+used as a state identifier in the HTTP header E-Tag field for
+requests/responses to avoid race conditions.
 
 
 Resource representation
 +++++++++++++++++++++++
 
-The key difference of REST approach from others API is instead having one URI
-for all our requests, REST demand separate service by resources with unique
-URI. Each of them should have limited amount of stateless and standard HTTP
+The key difference of using REST instead of others API is that REST
+requires separation of services via resources with unique URIs. Each
+of them should have limited amount of state and support standard HTTP
 methods: GET, POST, DELETE, PUT.
 
-For example in Ganeti case we can have a set of URI:
- - /{clustername}/instances
- - /{clustername}/instances/{instancename}
- - /{clustername}/instances/{instancename}/tag
- - /{clustername}/tag
+For example in Ganeti's case we can have a set of URI:
+
+ - ``/{clustername}/instances``
+ - ``/{clustername}/instances/{instancename}``
+ - ``/{clustername}/instances/{instancename}/tag``
+ - ``/{clustername}/tag``
 
-A GET request to /{clustername}/instances will return list of instances, a POST
-to /{clustername}/instances should create new instance, a DELETE
-/{clustername}/instances/{instancename} should delete instance, a GET
-/{clustername}/tag get cluster tag
+A GET request to ``/{clustername}/instances`` will return the list of
+instances, a POST to ``/{clustername}/instances`` should create a new
+instance, a DELETE ``/{clustername}/instances/{instancename}`` should
+delete the instance, a GET ``/{clustername}/tag`` should return get
+cluster tags.
 
-Each resource URI has a version prefix. The complete list of resources id TBD.
+Each resource URI will have a version prefix. The resource IDs are to
+be determined.
 
-Internal encoding might be JSON, XML, or any other. The JSON encoding fits
-nicely in Ganeti RAPI needs. Specific representation client can request with
-Accept field in the HTTP header.
+Internal encoding might be JSON, XML, or any other. The JSON encoding
+fits nicely in Ganeti RAPI needs. The client can request a specific
+representation via the Accept field in the HTTP header.
 
-The REST uses standard HTTP as application protocol (not just as a transport)
-for resources access. Set of possible result codes is a subset of standard HTTP
-results. The stateless provide additional reliability and transparency to
-operations.
+REST uses HTTP as its transport and application protocol for resource
+access. The set of possible responses is a subset of standard HTTP
+responses.
+
+The statelessness model provides additional reliability and
+transparency to operations (e.g. only one request needs to be analyzed
+to understand the in-progress operation, not a sequence of multiple
+requests/responses).
 
 
 Security
 ++++++++
 
-With the write functionality security becomes much bigger an issue.  The Ganeti
-RAPI uses basic HTTP authentication on top of SSL connection to grant access to
-an exported resource. The password stores locally in Apache-style .htpasswd
-file. Only one level of privileges is supported.
+With the write functionality security becomes a much bigger an issue.
+The Ganeti RAPI uses basic HTTP authentication on top of an
+SSL-secured connection to grant access to an exported resource. The
+password is stored locally in an Apache-style ``.htpasswd`` file. Only
+one level of privileges is supported.
+
+Caveats
++++++++
+
+The model detailed above for job submission requires the client to
+poll periodically for updates to the job; an alternative would be to
+allow the client to request a callback, or a 'wait for updates' call.
+
+The callback model was not considered due to the following two issues:
 
+- callbacks would require a new model of allowed callback URLs,
+  together with a method of managing these
+- callbacks only work when the client and the master are in the same
+  security domain, and they fail in the other cases (e.g. when there is
+  a firewall between the client and the RAPI daemon that only allows
+  client-to-RAPI calls, which is usual in DMZ cases)
+
+The 'wait for updates' method is not suited to the HTTP protocol,
+where requests are supposed to be short-lived.
 
 Command line changes
 ~~~~~~~~~~~~~~~~~~~~
 
 Ganeti 2.0 introduces several new features as well as new ways to
 handle instance resources like disks or network interfaces. This
-requires some noticable changes in the way commandline arguments are
+requires some noticeable changes in the way command line arguments are
 handled.
 
-- extend and modify commandline syntax to support new features
-- ensure consistent patterns in commandline arguments to reduce cognitive load
+- extend and modify command line syntax to support new features
+- ensure consistent patterns in command line arguments to reduce
+  cognitive load
 
 The design changes that require these changes are, in no particular
 order:
@@ -1437,7 +1572,7 @@ order:
   cluster, each supporting different parameters,
 - support for device type CDROM (via ISO image)
 
-As such, there are several areas of Ganeti where the commandline
+As such, there are several areas of Ganeti where the command line
 arguments will change:
 
 - Cluster configuration
@@ -1452,7 +1587,8 @@ arguments will change:
   - handling of CDROM devices and
   - handling of hypervisor specific options.
 
-There are several areas of Ganeti where the commandline arguments will change:
+There are several areas of Ganeti where the command line arguments
+will change:
 
 - Cluster configuration
 
@@ -1552,7 +1688,7 @@ device type specific options supported:
 :--net: for network interface cards
 :--disk: for disk devices
 
-The syntax to the device specific options is similiar to the generic
+The syntax to the device specific options is similar to the generic
 device options, but instead of specifying a device number like for
 gnt-instance add, you specify the magic string add. The new device
 will always be appended at the end of the list of devices of this type
@@ -1584,11 +1720,13 @@ options supported:
 :--net: for network interface cards
 :--disk: for disk devices
 
-The syntax to the device specific options is similiar to the generic
+The syntax to the device specific options is similar to the generic
 device options. The device number you specify identifies the device to
 be modified.
 
-Example: gnt-instance modify --disk 2:access=r
+Example::
+
+  gnt-instance modify --disk 2:access=r
 
 Hypervisor Options
 ++++++++++++++++++
@@ -1596,8 +1734,8 @@ Hypervisor Options
 Ganeti 2.0 will support more than one hypervisor. Different
 hypervisors have various options that only apply to a specific
 hypervisor. Those hypervisor specific options are treated specially
-via the --hypervisor option. The generic syntax of the hypervisor
-option is as follows:
+via the ``--hypervisor`` option. The generic syntax of the hypervisor
+option is as follows::
 
   --hypervisor $HYPERVISOR:$OPTION=$VALUE[,$OPTION=$VALUE]
 
@@ -1608,7 +1746,7 @@ option is as follows:
 :$VALUE: hypervisor option value, string
 
 The hypervisor option for an instance can be set on instance creation
-time via the gnt-instance add command. If the hypervisor for an
+time via the ``gnt-instance add`` command. If the hypervisor for an
 instance is not specified upon instance creation, the default
 hypervisor will be used.
 
@@ -1616,15 +1754,16 @@ Modifying hypervisor parameters
 +++++++++++++++++++++++++++++++
 
 The hypervisor parameters of an existing instance can be modified
-using --hypervisor option of the gnt-instance modify command. However,
-the hypervisor type of an existing instance can not be changed, only
-the particular hypervisor specific option can be changed. Therefore,
-the format of the option parameters has been simplified to omit the
-hypervisor name and only contain the comma separated list of
-option-value pairs.
+using ``--hypervisor`` option of the ``gnt-instance modify``
+command. However, the hypervisor type of an existing instance can not
+be changed, only the particular hypervisor specific option can be
+changed. Therefore, the format of the option parameters has been
+simplified to omit the hypervisor name and only contain the comma
+separated list of option-value pairs.
 
-Example: gnt-instance modify --hypervisor
-cdrom=/srv/boot.iso,boot_order=cdrom:network test-instance
+Example::
+
+  gnt-instance modify --hypervisor cdrom=/srv/boot.iso,boot_order=cdrom:network test-instance
 
 gnt-cluster commands
 ++++++++++++++++++++
@@ -1664,7 +1803,8 @@ further changes):
 Hypervisor cluster defaults
 +++++++++++++++++++++++++++
 
-The generic format of the hypervisor clusterwide default setting option is:
+The generic format of the hypervisor cluster wide default setting
+option is::
 
   --hypervisor-defaults $HYPERVISOR:$OPTION=$VALUE[,$OPTION=$VALUE]
 
@@ -1673,39 +1813,40 @@ The generic format of the hypervisor clusterwide default setting option is:
 :$OPTION: cluster default option, string,
 :$VALUE: cluster default option value, string.
 
+Glossary
+========
 
-Functionality changes
----------------------
+Since this document is only a delta from the Ganeti 1.2, there are
+some unexplained terms. Here is a non-exhaustive list.
 
-The disk storage will receive some changes, and will also remove
-support for the drbd7 and md disk types. See the
-design-2.0-disk-changes document.
+.. _HVM:
 
-The configuration storage will be changed, with the effect that more
-data will be available on the nodes for access from outside ganeti
-(e.g. from shell scripts) and that nodes will get slightly more
-awareness of the cluster configuration.
+HVM
+  hardware virtualization mode, where the virtual machine is oblivious
+  to the fact that's being virtualized and all the hardware is emulated
 
-The RAPI will enable modify operations (beside the read-only queries
-that are available today), so in effect almost all the operations
-available today via the ``gnt-*`` commands will be available via the
-remote API.
+.. _LU:
 
-A change in the hypervisor support area will be that we will support
-multiple hypervisors in parallel in the same cluster, so one could run
-Xen HVM side-by-side with Xen PVM on the same cluster.
+LogicalUnit
+  the code associated with an OpCode, i.e. the code that implements the
+  startup of an instance
 
-New features
-------------
+.. _opcode:
+
+OpCode
+  a data structure encapsulating a basic cluster operation; for example,
+  start instance, add instance, etc.;
+
+.. _PVM:
 
-There will be a number of minor feature enhancements targeted to
-either 2.0 or subsequent 2.x releases:
+PVM
+  para-virtualization mode, where the virtual machine knows it's being
+  virtualized and as such there is no need for hardware emulation
 
-- multiple disks, with custom properties (read-only/read-write, exportable,
-  etc.)
-- multiple NICs
+.. _watcher:
 
-These changes will require OS API changes, details are in the
-design-2.0-os-interface document. And they will also require many
-command line changes, see the design-2.0-commandline-parameters
-document.
+watcher
+  ``ganeti-watcher`` is a tool that should be run regularly from cron
+  and takes care of restarting failed instances, restarting secondary
+  DRBD devices, etc. For more details, see the man page
+  ``ganeti-watcher(8)``.
