Commit 00d264c5 authored by Gary Wong's avatar Gary Wong

Add detailed geni-lib documentation (especially for the code samples).

parent f7c8f2be
"""An example of constructing a profile with node IP addresses specified
manually.
Instructions:
Wait for the profile instance to start, and then log in to either VM via the
ssh ports specified below. (Note that even though the EXPERIMENTAL
data plane interfaces will use the addresses given in the profile, you
will still connect over the control plane interfaces using addresses
given by the testbed. The data plane addresses are for intra-experiment
communication only.)
"""
import geni.portal as portal
import geni.rspec.pg as pg
import geni.rspec.igext as ext
pc = portal.Context()
rspec = pg.Request()
......@@ -30,8 +41,4 @@ link.addInterface(iface2)
rspec.addResource(link)
tour = ext.Tour()
tour.Description( ext.Tour.TEXT, "An example of constructing a profile with node IP addresses specified manually." )
rspec.addTour( tour )
pc.printRequestRSpec(rspec)
"""An example of constructing a profile with install and execute services.
manually.
Instructions:
Wait for the profile instance to start, and then log in to the VM via the
ssh port specified below. The install and execute services are handled
automatically during profile instantiation, with no manual intervention
required.
"""
import geni.portal as portal
import geni.rspec.pg as pg
import geni.rspec.igext as ext
pc = portal.Context()
rspec = pg.Request()
......@@ -11,13 +20,9 @@ node1 = pg.XenVM("node1")
node1.disk_image = "<URL to disk image>"
# Install and execute scripts on the VM
node1.addService(pg.Install(url="<URL to the tarball file>", path="local"))
node1.addService(pg.Execute(shell="bash", command="<Path to executable>"))
node1.addService(pg.Install(url="http://example.org/sample.tar.gz", path="/local"))
node1.addService(pg.Execute(shell="bash", command="/local/example.sh"))
rspec.addResource(node1)
tour = ext.Tour()
tour.Description( ext.Tour.TEXT, "An example of constructing a profile with install and execute services." )
rspec.addTour( tour )
pc.printRequestRSpec(rspec)
"""An example of using parameters to construct a profile with a variable
number of nodes.
Instructions:
Wait for the profile instance to start, and then log in to one or more of
the VMs via the ssh port(s) specified below.
"""
# Import the Portal object.
import geni.portal as portal
# Import the ProtoGENI library.
import geni.rspec.pg as pg
# Create the Portal context.
pc = portal.Context()
# Describe the parameter(s) this profile script can accept.
pc.defineParameter( "n", "Number of VMs", portal.ParameterType.INTEGER, 1 )
# Retrieve the values the user specifies during instantiation.
params = pc.bindParameters()
# Create a Request object to start building the RSpec.
rspec = pg.Request()
# Check parameter validity.
if params.n < 1 or params.n > 8:
pc.reportError( portal.ParameterError( "You must choose at least 1 and no more than 8 VMs." ) )
for i in range( params.n ):
# Create a XenVM and add it to the RSpec.
node = pg.XenVM( "node" + str( i ) )
rspec.addResource( node )
# Print the RSpec to the enclosing page.
pc.printRequestRSpec(rspec)
"""An example of constructing a profile with a single raw PC.
Instructions:
Wait for the profile instance to start, and then log in to the host via the
ssh port specified below.
"""
# Import the Portal object.
import geni.portal as portal
# Import the ProtoGENI library.
import geni.rspec.pg as pg
# Import the extension library (used for the profile description).
import geni.rspec.igext as ext
# Create the Portal context.
pc = portal.Context()
......@@ -15,10 +20,5 @@ rspec = pg.Request()
node = pg.RawPC("node")
rspec.addResource(node)
# Create a profile description, and add it to the RSpec.
tour = ext.Tour()
tour.Description( ext.Tour.TEXT, "An example of constructing a profile with a single raw PC." )
rspec.addTour( tour )
# Print the RSpec to the enclosing page.
pc.printRequestRSpec(rspec)
"""An example of constructing a profile with a single Xen VM.
Instructions:
Wait for the profile instance to start, and then log in to the VM via the
ssh port specified below. (Note that in this case, you will need to access
the VM through a high port on the physical host, since we have not requested
a public IP address for the VM itself.)
"""
# Import the Portal object.
import geni.portal as portal
# Import the ProtoGENI library.
import geni.rspec.pg as pg
# Import the extension library (used for the profile description).
import geni.rspec.igext as ext
# Create the Portal context.
pc = portal.Context()
......@@ -15,10 +22,5 @@ rspec = pg.Request()
node = pg.XenVM("node")
rspec.addResource(node)
# Create a profile description, and add it to the RSpec.
tour = ext.Tour()
tour.Description( ext.Tour.TEXT, "An example of constructing a profile with a single Xen VM." )
rspec.addTour( tour )
# Print the RSpec to the enclosing page.
pc.printRequestRSpec(rspec)
"""An example of constructing a profile with two ARM64 nodes connected by a LAN.
Instructions:
Wait for the profile instance to start, and then log in to either host via the
ssh ports specified below.
"""
import geni.portal as portal
import geni.rspec.pg as pg
import geni.rspec.igext as ext
pc = portal.Context()
rspec = pg.Request()
......@@ -30,9 +36,4 @@ link.addInterface(iface2)
# Add the link to the RSpec.
rspec.addResource(link)
# Create a profile description, and add it to the RSpec.
tour = ext.Tour()
tour.Description( ext.Tour.TEXT, "An example of constructing a profile with two ARM64 nodes connected by a LAN." )
rspec.addTour( tour )
pc.printRequestRSpec(rspec)
"""An example of constructing a profile with two VMs connected by a LAN.
Instructions:
Wait for the profile instance to start, and then log in to either VM via the
ssh ports specified below.
"""
import geni.portal as portal
import geni.rspec.pg as pg
import geni.rspec.igext as ext
pc = portal.Context()
rspec = pg.Request()
# Create a XenVM nodes.
# Create two XenVM nodes.
node1 = pg.XenVM("node1")
node2 = pg.XenVM("node2")
......@@ -26,9 +32,4 @@ link.addInterface(iface2)
# Add the link to the RSpec.
rspec.addResource(link)
# Create a profile description, and add it to the RSpec.
tour = ext.Tour()
tour.Description( ext.Tour.TEXT, "An example of constructing a profile with two VMs connected by a LAN." )
rspec.addTour( tour )
pc.printRequestRSpec(rspec)
......@@ -69,7 +69,7 @@ be rebooted in order to take consistent snapshots of the disk.
For the time being, this process only works for single-node profiles; we will
add support for multi-node profiles in the future.
@subsection{Creating the Profile}
@subsection[#:tag "creating-the-profile"]{Creating the Profile}
@itemlist[ #:style 'ordered
@instructionstep["Create an experiment"]{Create an experiment using the
......
......@@ -34,23 +34,167 @@ about @tt{geni-lib} and additional examples, can be found in the
@code-sample["geni-lib-single-vm.py"]
This example demonstrates the two most important objects: the @bold{portal
context} (acquired with the @tt{portal.Context()} constructor in the
@tt{geni.portal} module), and the @bold{request RSpec} created with
@tt{pg.Request()} from @tt{geni.rspec.pg}. These fundamental objects
are central to essentially all @(tb) @tt{geni-lib} profiles.
Once the request object has been created, arbitrary resources may be
added to it using the RSpec's @tt{addResource} method. In this example,
just a single node (created with the @tt{pg.XenVM()} constructor,
asking for a single VM identified by the name "node") is requested,
but in more complicated profiles, the @tt{addResource} call could be
repeated as many times as necessary with different resources.
The final action the @tt{geni-lib} script performs is to generate the
XML representation of the request RSpec, with the @tt{printRequestRSpec}
call on the last line. This has the effect of communicating the
description of all the resources requested by the profile back to
@(tb).
You will also notice that the profile begins with a string literal
(to be precise, it is a Python
@link["http://www.python.org/dev/peps/pep-0257/"]{docstring}). The initial
text will also be used as the
@seclink["creating-the-profile"]{profile description}; the text following
the @tt{Instructions:} line will be used as the corresponding
@seclink["creating-the-profile"]{instructions}. This documentation
is so important that adding the description to the profile is mandatory.
(Using a docstring like this is not the only way to produce the description
and instructions, although it is the most convenient.)
This simple example has now demonstrated all the important elements of
a @tt{geni-lib} profile. The portal context and request RSpec objects,
the final @tt{printRequestRSpec} call, and the docstring description
and instructions are ``boilerplate'' constructions, and you will probably
include similar or identical versions of them in every @tt{geni-lib}
profile you create unless you are doing something quite unusual. The
@tt{addResource} call, however, and its corresponding @tt{XenVM} objects
in this example, is very much specific to this example and you should
expect to tailor this portion of the script in each profile.
@section[#:tag "geni-lib-example-single-pc"]{A single physical host}
@code-sample["geni-lib-single-pc.py"]
As mentioned above, most of these simple examples consist of boilerplate
@tt{geni-lib} fragments, and indeed the portal context and request RSpec
operations are unchanged from the previous script. The big difference,
though (other than the updated documentation) is that in this case the
@tt{RawPC()} constructor from the @tt{pg} module was invoked instead of
the @tt{XenVM()}. As you might expect, the new profile will request a
physical host instead of a virtual one. (A side effect of using a
real machine is that it automatically comes with a unique public IP address,
where the VM used in the earlier example did not. Profiles can
@seclink["public-ip-access"]{request public IP addresses} for VMs too,
though it does not happen by default.)
@section[#:tag "geni-lib-example-two-vm-lan"]{Two XenVM nodes with a LAN between them}
@code-sample["geni-lib-two-vm-lan.py"]
This example demonstrates two important @tt{geni-lib} concepts: first,
adding more than a single node to the request (which is a relatively
straightforward matter of calling more than one node object constructor,
being careful to use a different identifier each time, and then invoking
@tt{addResource} once per node). It also shows how to add @bold{links}
between nodes, which is slightly more involved than the nodes themselves,
because the network topology must be described in enough detail to be
unambiguous.
Note that every node which will be included in a network requires
at least one @bold{interface}. (This is accomplished in the example
above with the @tt{addInterface} method available on the node object.)
Next, the link itself is constructed (see the invocation of @tt{pg.LAN}),
and then both of the previously described interfaces are added to the
link with its @tt{addInterface} method. Note that the @bold{link's}
@tt{addInterface} and the @bold{node's} @tt{addInterface} are distinct
methods, even though they share the same name! They are also both
strictly necessary to describe the topology: although it might seem
redundant to add the same interfaces in more than one place in this case,
observe that in a more complicated profile containing more than one
network, establishing the correct correspondence between interfaces and
links would become critical.
Lastly, the network object itself is a kind of resource, and it must
be added to the request RSpec just as the nodes were. Calling
@tt{addResource} and passing the link object takes care of this.
@section[#:tag "geni-lib-example-two-arm-lan"]{Two ARM64 servers in a LAN}
@code-sample["geni-lib-two-arm-lan.py"]
We now come to demonstrate requesting particular properties of nodes---until
now, all nodes had been either @tt{XenVM()}s or @tt{RawPC()}s and (although
they might have had @seclink["geni-lib-example-two-vm-lan"]{interfaces added})
nothing further was said about them. @tt{geni-lib} allows the user to
specify various details about the nodes, and this example makes use
of the @tt{hardware_type} property. The @tt{hardware_type} can be set
to a string describing the type of physical machine onto which the logical
node can be mapped: in this case, the string is @tt{"m400"}, which means
a ProLiant Moonshot m400 host (an ARM64 server). Obviously, such a
profile cannot be instantiated on a cluster without a sufficient quantity
of appropriate machines! (This profile was written with the
@seclink["cloudlab-utah"]{Utah CloudLab} cluster in mind.) @(tb)
will indicate a list of suitable clusters when the user attempts to
instantiate the profile, so he or she is not forced to find one by
trial and error.
@section[#:tag "geni-lib-example-node-ips"]{Set a specific IP address on each node}
@code-sample["geni-lib-node-ips.py"]
Some of the available qualifiers on requested nodes are specified by
manipulating attributes within the node (or interface) object directly. The
@tt{hardware_type} in the
@seclink["geni-lib-example-two-arm-lan"]{previous example} is one
such case, as is the @tt{component_id} here. (Note that the @tt{component_id}
in this example is applied to an interface, although it is also possible to
specify @tt{component_id}s on nodes, too, to request a particular
physical host.)
Other modifications to requests require dedicated methods. For instance,
see the @tt{addAddress()} calls made on each of the two interfaces above.
In each case, an @tt{IPv4Address} object is obtained from the appropriate
constructor (the parameters are the address and the netmask, respectively),
and then added to the corresponding interface.
@section[#:tag "geni-lib-example-os-install-scripts"]{Specify an operating system and set install and execute scripts}
@code-sample["geni-lib-os-install-scripts.py"]
This example demonstrates how to request @bold{services} for a node,
where @(tb) will automate some task as part of the profile instance
setup procedure. In this case, two services are described (an
@bold{install} and an @bold{execute}). This is a very common pair of services
to request together: the @tt{Install()} object describes a service which
retrieves a tarball from the location given in the @tt{url} parameter,
and installs it into the local filesystem as specified by @tt{path}.
(The installation occurs during node setup, upon the first boot after the
disk image has been loaded.) The second service, described by the
@tt{Execute()} object, invokes a @tt{shell} process to run the given
@tt{command}. In this example (as is common), the command refers directly
to a file saved by the immediately preceding @tt{Install} service. This
behaviour works, because @(tb) guarantees that all @tt{Install} services
complete before any @tt{Execute} services are started.
@section[#:tag "geni-lib-example-parameters"]{Profiles with user-specified parameters}
@code-sample["geni-lib-parameters.py"]
Until now, all of the @tt{geni-lib} scripts have described profiles
which could also have been generated with @seclink["jacks"]{the Jacks GUI},
or even by writing a @seclink["rspecs"]{raw XML RSpec} directly. However,
@tt{geni-lib} profiles offer an important feature unavailable by the
other methods: the ability to describe not a static request, but a
request ``template'' which is dynamically constructed based on a
user's choices at the time the profile is instantiated. The
mechanism for constructing such profiles relies on profile @bold{parameters};
the @tt{geni-lib} script describes the set of parameters it will accept,
and then retrieves the corresponding values at instantiation time
and is free to respond by constructing arbitrarily different resource
requests based on that input.
The profile above accepts exactly one parameter---the number of VMs it
will instantiate.
......@@ -103,7 +103,7 @@ Start by pointing your browser at @url[(apturl)].
@apt-only{If you have never used this email address with @(tb) before (or if
you switch computers or browsers), @(tb) will send a verification email.
Watch your email and enter the code into @(tb) when prompted. (If it
doesn't arrive in a few minutes, check your spam folder!)"}
doesn't arrive in a few minutes, check your spam folder!)}
}
@instructionstep["Use your experiment"]{
When your experiment is ready to use, the progress bar will be complete, and
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment