It's neither comprehensive nor definitive but it will certainly help you get started
Before we answer the why let us first deal with the what.
"What is openresty?"
Openresty is a packaging of nginx together with various useful libraries that can be used to write application servers. Notice the emphasis on write. Now you will not be limited to just configuring your server but you can program it. At it's core it's still the nginx that you know. All your configuration files that work with vanilla nginx will work with openresty. When you install openresty you lose nothing. But you gain:-
With that out of the way.. why would you want to learn about open resty at all? Aren't there enough web frameworks already? Do you need to learn yet another one?
Fair questions. Nginx does web applications really really well. It is light, it is fast and it is very well documented. People have often reported that just by configuring nginx to serve their applications they have gained a boost in performance. That nginx makes your applications fast is a well accepted fact. What openresty does is that it takes all the goodness of nginx : it's fast response time and it's low memory usage and removes the barriers to developing applications with it.
"And what are these barriers?"
The configuration files themselves. While they are quite flexible all they do is they limit the power of nginx to the set that is configurable. Sure you can do a lot but not anything beyond what is allowed. Here is a simple example for instance
Suppose before forwarding the data POSTed
to your application you want to do certain checks on the data so that you can be sure that your application receives only the clean refined data to operate upon. Is there any easy way to validate the data posted to nginx before it is forwarded to your application server? If there is I could not find it. But in open resty this is as simple as
content_by_lua '
-- read the request of the body
ngx.req.read_body()
-- give back the body as an easily query-able table data structure
local post_args = ngx.req.get_post_args()
-- validate the data
local clean_body_data = require("lib/validate").validate_body(post_args)
'
This has the effect of simplifying your architecture by guaranteeing that any data that is posted by the proxy is valid. So your application layer can focus on working upon it without worrying about cleansing it first. Once you learn open resty you will be able to identify many such functions that can be better delegated to a proxy. It will simplify your application and allow you to use nginx and all it's low resource, fast performance goodness to the fullest. Win win.
"But my application is working. I don't want to change any thing"
I am not asking you to change. Remember that openresty is still nginx. Everything that was working will continue to work. Openresty just gives you an opportunity to make use of nginx in ways that you might not have considered before. You have nothing to loose and much to gain.
If you are still not convinced check out this Top ten things about openresty post to get a quick idea of what all you'll be able to do with it once you've put in the effort of learning it.
What do you need to start learning open resty?
You need to know lua. BUT BEFORE YOU CLOSE THIS TAB know that you don't need to have a mastery of the language to start developing openresty applications. All you need is 15 minutes worth of lua and you are all set to go. More specifically you need to have a solid understanding of lua tables, lua modules, how to write loops and conditional statements and the variable scope in lua. If you have programmed before you can learn all of this before you finish your first cup of coffee :)
But wait I don't know any nginx
Not a problem. Nginx applications are written in what are known as configuration files. You can get most of it by just reading but if you encounter any problem the nginx website has a very detailed documentation and tonnes of community support so you should be able to google your way out of any trouble. The entire guide is written with the assumption that
All you need therefore to follow this guide is willingness to learn.
In case you skipped the introduction and jumped to the hello world then first of all good for you! and second a summary of what I said above:-
Openresty is just an enhancement of nginx. So all your previous knowledge of nginx carries over.
This is a hello_world example to illustrate how to write openresty scripts. The first thing that we will do is we will create a file structure for our config files and scripts. Here is a high level overview
root
-logs
-conf
All the nginx configuration files go within the conf
folder and all the logging goes in the logs
folder.
Easy right? This structure will not only do for our
simple demonstration here but will also hold well for complex examples
that we will work on later. Let us examine the configuration file for the hello_world example
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
location / {
default_type text/html;
content_by_lua '
ngx.say("
hello, world
")
';
}
}
}
This will look very familiar to you if you have used nginx.
The only thing that is new in this file is the content_by_lua
statement.
This statement executes the lua code that within the ''
string.
The nginx api is provided to lua in the form of a standard package ngx
.
Well there are in fact two packages ngx
and ndk
but we will focus on ngx
for now as we can get far with just that. What ngx.say()
does is
that it sends the concatenated arguments to the client as an http response.
But enough talk for now.
Now I don't know about you but wrapping code scripts
around strings does not look pretty to me.
Wouldn't it be nice if we could create a separate file for
lua scripts and include it in the nginx configuration files?
content_by_lua_file
allows us to do just that. Let us create a separate directory lua
mkdir lua
And now create a lua file called hello_world.lua
containing just one line of code
ngx.say("
Hello world from a lua file
");
Now we will create another location block in the nginx.conf
file that will use content_by_lua_file
instead of content_by_lua
.
Here is what the final configuration looks like
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
location / {
default_type text/html;
content_by_lua '
ngx.say("
hello, world
")
';
}
location /by_file {
default_type text/html;
content_by_lua_file ./lua/hello_world.lua;
}
}
}
Before we curl for the /by_file
location we must reload nginx
nginx -p pwd -s reload
now curl http://localhost:8080/by_file
should return

hello world from lua

Some notes
require()
the file and use it's functions. We discuss more on this in the organizing your lua code section.Nginx comes with it's own mini programming language in which directives form the basic constructs.
These directives are either block level or simple. Simple directives are
structured as the name of the directive and the name of the parameters.
The directive name and the parameters are separated by a space and the end of the
directive is marked by a ;
. The block level directives are similar in with
the only difference being it is marked by {
and
}
brackets. Within a block level directive there could be one or more
simple directives. These directives make up the configuration files. Time for an example:-
#location is a block level directive
location /{
#proxy_pass is a simple directive
proxy_pass http://localhost:5984/;
}
Openresty keeps the same structuring of the configuration files. You still create configuration files with simple and block level directives. Any nginx directive works with openresty in the same way as it would in a vanilla nginx application. However a new set of directives are introduced by openresty that serve as the entry point to the lua code.
Openresty's directives serve as the entry point for execution of lua code.
As we saw in the last chapter there is little difference between
a vanilla nginx configuration and an nginx_lua configuration. In fact almost
all of the nginx directives can be used as usual in an nginx_lua
configuration
file. However openresty adds several new directives that enhances the configurability
of nginx. We already looked at content_by_lua
and content_by_lua_file
.
Here we will take a look at a few more interesting ones
This directive turns the caching of lua modules on or off. By default the
the caching is turned on so that the lua modules are loaded once and then
just reused. This is a desirable effect as we would not want to reload
modules on every request. So when would you want to turn the caching off?
During the development phase. It can be a pain
to edit the configuration file, save it and then do an nginx -s reload
over and over again.
When caching is turned off the module is reloaded
on every request so you can just edit, save your file and then
"refresh" the browser to see the changes. Just be sure to turn the lua_code_cache
on again in production.
Let us test this by turning lua_code_cache
off in our hello world example.
After the changes our configuration file should look something like this.
worker_processes 1;
error_log /dev/stderr;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
location /by_file {
default_type text/html;
lua_code_cache off; #only for development
content_by_lua_file ./lua/hello_world.lua; #update it with path to your lua file
}
}
}
Now lets run this by
nginx -p 'pwd' -c conf/nginx.conf
You should see an alert message displayed on your console
lua_code_cache is off; this will hurt performance in ./directives/conf/nginx.conf
Disregard the message for now. But keep in mind to turn on the code cache in production.
Then curl http://localhost:8080/
you should see
hello world
in response
Now edit the hello_world.lua to
ngx.say("hello world");
and on curl http://localhost:8080/
you should see the response
hello world
This makes our work flow of developing ngx_lua applications much smoother.
Note as you might have already guessed lua_code_cache
works only for
*by_lua_file
directives. It will have no effect upon *by_lua
directives.
nginx block level rules apply to the lua_code_cache
directive. Which
means if you set lua_code_cache
off in a top level directive all the other
lower lever directives will pick up on this configuration. Similarly you can achieve
isolation for particular directives by turning the caching off only
for those directives.
Also any changes you make to the nginx.conf file itself won't be detected automatically by nginx. And you will have to reload the server manually. This automatic reload works only for lua files.
init_by_lua directive runs the lua code specified by the parameter string on a global level. As the name probably suggests the init_by_lua runs the lua code as nginx is initializing (loading the configuration files). As a result we can declare parameters here that will be used by directives in the configuration files after they are loaded. Thus init_by_lua is useful when you want to register lua global variables or start lua modules during the nginx server start up.
Before we take a look at the example of init_by_lua
a friendly warning.
As a general principle refrain from using lua global variables. For most variables you should use a local
keyword to declare lua variables local to it's scope. And remember any variable declared
without a local
keyword is global in lua.
local cat = "meow" -- use this form most of the time
dog = "woof" -- avoid this form except for when it makes sense
Why refrain from using global variables?
Openresty is based on the principle of request isolation. Any request that goes to a location
block say location /one{}
is independent from the request that goes to the location /two{}
. Every
request handler has it's own set of global variables that are deleted at the end of the request cycle.
This means that if you declare a variable as global in a location block that global variable
will be inaccessible from another location block. Since as we discussed above each location block
has it's own set of global variables that are isolated from each other.
When does it make sense to use global variables?
Suppose you want to use a module that parses JSON which will be used
in many handlers, or a database client that will be used in many handlers
or any other module that will be used across many handlers then it makes sense
to declare the variable global and even then init_by_lua should be the only place
where you do it.
An example to illustrate the difference b/w local and global variables
init_by_lua '
cjson = require("cjson") -- cjson is a global variable
'
location /one {
content_by_lua '
local validate = require("lua/validate") -- validate is a local variable
decoded_one = cjson.decode({hello="world"}) --decoded_one is a global variable
'
}
location /two{
content_by_lua '
ngx.say(cjson.encode(decoded_one))
'
}
In the example above we have two global variables. The first one cjson
in the init_by_lua is
a true global variable. One that can be accessed across request handlers.
The second global variable (decoded_one)
that we declare in the location /one
block is global only in the context of /one
if we try to access
this in the /two
block we get a nil value. Besides having unexpected effects,that are hard to debug,
global variables have performance
penalties of being looked up from a global table.
Conclusion: global variables are to be used sparingly for things
we want to be truly global. init_by_lua should be the only place to declare them.
init_by_lua
has a variant in init_by_lua_file
where you can supply a file
containing the lua code that will be run in a global context.
Everything that we talked about in init_by_lua
also applies to init_by_lua_file
.
location /json{
content_by_lua '
ngx.say(cjson.encode({message="hello world",another_message="goodbye world"}));
';
}
In the location block above we are using cjson
global
variable that was declared in an init_by_lua_file
to encode
lua tables in json. cjson
was a good contender for the global
variable declaration because we are going to be using it in many
location blocks.
set_by_lua
directive is equivalent to nginx's
set commands.
Quite predictably set is used in nginx to 'set' the value for a variable.
Similarly set_by_lua
allows you to set a variable by evaluating a lua code string.
Once more set_by_lua
has a _file alternative in set_by_lua_file
that allows you to set a variable
by executing the code in lua file.
Note set_by_lua
blocks the nginx's event loop during
it's execution therefore long time consuming
code sequences are best avoided here.
So while a few arithmetic computations are fine loops should be avoided.
set_by_lua
works well with nginx's set
command so the two can be used interchangeably.
The directives will run in the order in which they appear in the code.
location /set_by_lua{
set $nvar 20;
set_by_lua $lvar 'return ngx.var.nvar+1';
echo "$nvar,$lvar";
}
We already discussed this directive in the hello_ world chapter. But now that we have more understanding of how the directives work we can see content_by_lua in a different light.
Unlike the set_by_lua command that blocks the nginx's event loop content_by_lua directive
runs in it's own spawned coroutine. Which means that it does not block the nginx's event loop
and runs in a separate environment. content_by_lua is a special class of directive called content handlers.
They execute only in the context of location
directive (which if you recall our discussion at the beginning
of this chapter is a block level directive).
Anyway, the location directive captures the request for a matching url and then leaves it to the content handler to service the request. For instance
location /me
{
root /data/me;
}
if a request is made to /me
handle the content in /data/me
would be served.
A location block should have only one content handler. Do not mix nginx's
default content handlers like proxy_pass or directory mappers with ngx_lua's content_by_lua.
The content_by_lua
directive can execute lua code that is contained within it. For ex:
location /test{
content_by_lua '
local is_test = ngx.req.get_uri_args()["test"]
';
}
The above content_by_lua
directive executes a single line of lua code which
just stores the value of query string parameter called test in a local variable called is_test.
And yeah there is a content_by_lua_file
:)
ngnx_lua equivalent of nginx's
HttpRewriteModule.
Like the content_by_lua
directive rewrite_by_lua
runs in a spawned coroutine.
The important thing to keep in mind here is that the directive always runs after the
standard http_rewrite_module
. So if you are using both keep that in mind.
The purpose of rewrite_by_lua is to lua-fy the nginx rewrite phase which is basically used to:-
change request URI using regular expressions, return redirects, and conditionally select configurations.
rewrite_by_lua
can make api calls
(we will see how to make api calls in the next chapter).
So you can issue dynamic rewrites
based on the response returned from your database etc.
About rewrite_by_lua_file
: .....
It's same as rewrite_by_lua
except that it
executes lua code from a file.
ngx_lua equivalent of HttpAccessModule
Two points to note here
It runs in a separate coroutine.
It runs after the standard nginx http_access module
so if you are mixing the two
keep that in mind.
Like rewrite_by_lua
it can make api calls.
And yes there is a access_by_lua_file
By now you have probably understood what lua directives are and how they work. There are a few more directives.Some of them are utilities and some for tweaking the ngx_lua behaviour. The important points to take away from this chapter are
The ones I have covered though are ones that you will be using most of the time. So instead of going on and on about the directives I will leave a link to the reference and you can study them at your convenience.
For now we move on to the meatier stuff. The ngx_lua API.
Ah the nginx API. This is where the magic happens.
Remember all those by_lua
directives that we talked about in the last chapter?
They are all just there to execute lua code. The stuff that makes the lua code exiting to write are all the
packages provided within openresty that allow you to script and manipulate the behaviour of nginx.
In the words of the author of openresty:-
The various _by_lua and _by_lua_file configuration directives serve as gateways to the Lua API within the nginx.conf file
The nginx api is available in the form of two packages
ngx
and ndk
. These packages are in the global scope by default
and are always available to the lua directives. Which clears up a few
things for us. Remember how in *_by_lua_file
directives we
could simply call the methods on ngx
package even without requiring it?
As you can probably guess, the reason for it was that the ngx
package
was already available to the directive globally. So there
was no need for us to require
it.
Context: rewrite_by_lua, access_by_lua, content_by_lua
ngx.location.capture
is one of the most powerful functions in the lua ngx api. It allows you
to make subrequests to a uri(location).
What makes it so powerful you ask?
Remember the nginx location
directive?
No? A quick recap:- It defines endpoints for clients to make requests to. For instance
a location /hello
tells the client that if you make a request to /hello
I will give you
back the results of code executed inside the location
directive. And important point
to note here is that any request made by the client to this location
endpoint is
an http/tcp
request.
ngx.location.capture
issues an internal non blocking, synchronous request
to a location
. Unlike the clients request which has to be http the location_capture requests involve
no http overhead. It is just a fast and light internal c
level call while mimicking
the familiar http interface. Time for an example:-
suppose you have a location:
location /go-go-go{
###does something in a hurry
}
You can make a call to this endpoint using location capture like so"-
local res = ngx.location.capture("/go-go-go")
See how simple it is. Issue a request to the uri
already defined somewhere in a
location
directive and capture it's response.
Now one area where many people are confused (I admit I was too when I was first learnt about it) is that the uri must be internal. If you try to do
local res = ngx.location.capture("http://www.google.com/")
it won't work. Because the uri is external. However if you do
local res = ngx.location.capture("/google_proxy")
where /google_proxy is in the conf as
location /google_proxy{
proxy_pass http://www.google.com/;
}
then the request will work. It is important to understand that ngx.location.capture
works
only on internal requests (or on locations that are defined in the configuration files)
but that location can make an external http request.
So now back to the topic. The res
returned by ngx.location.capture
contains ..
well the response of the subrequest. Let us examine it.
res.status, res.body and res.truncated are pretty straightforward. res.header require more examination.
The response headers are simply a lua table. Here is what it might look like
{
Vary="Accept",
Etag='"E0KG1DRLA505ILRLPEJOPM679"'
}
So you access the header the same way in which you access a key from any lua table. For instance to access
the "vary" header we do res.header["Vary"]
.
Thus far we have seen how to make only simple requests
the internal uri using the ngx.location.capture
but the api
is far more flexible. You can pass query strings directly in
the url or by using the args option. For instance:-
local res= ngx.location.capture("/hello?a=1&b=2")
and
local res = ngx.location.caputre("/hello",{args={a=1,b=2}})
are equivalent. In both the cases a subrequest will be made to /hello?a=1&b=2
In fact the optional table can hold a lot more keys than just an args
.
local account_page = ngx.location.capture("/get_account",
{method=ngx.HTTP_POST,body=
json_body,args={user_name=name}
})
The above sample illustrates the point. Here the ngx.location.capture
passes in
While method,body and args are the ones that you will find yourself using more frequently just for the sake of completeness a list of all the optional keys that can be passed to the table can be found in the official docs.
context: rewrite_by_lua, access_by_lua, content_by_lua*
Oh yes :) Take the ngx.location.capture
and level it up by lua's superpowers.
One of the unique things about lua is that a function can have multiple return values. If this is your
first time working with lua then it might be a bit hard to to figure out when to use this feature. But
luckily ngx lua makes the application of this unique concept quite obvious. Check this out:-
local home,about,contact = ngx.location.capture.multi{{"/home"},{"/about"},{"/contact"}}
The above snippet of code is just like ngx.location.capture
that we talked about before. The
only difference here is that instead of making a subrequest to a single uri you can make
subrequests to multiple uris with a single line of code. Two important things to note here is that
1) These multiple subrequests run in parallel. 2) The results are returned when all the subrequests are completed.
So if you were thinking about piping the result of one subrequest into another then you are out of luck. But multiple requests are still pretty cool. Why?
Consider this scenario:- Say you have an application where you want to collect the interesting conversations across the Internet. You pull in data from various sources like twitter, facebook and linkedin. In this scenario you have to make at least three separate http requests to collect the data.
How would you do this in openresty? Like this:-
local statuses,tweets,posts = ngx.location.capture.multi{{"/facebook_graph"},{"/tweets"},{"/lnkedin"}}
and it all simply works. Now the application is not limited to external third party data aggregation.
It is quite probable that internally you use many applications that work on secondary tasks and maybe you
want to generate a report by querying them for updates.
Can one use an options table like in the nginx.location.capture?
Of course you can. Here is how:-
local res1,res2 = ngx.location.capture.multi{
{"/req1",{method=ngx.HTTP_POST}},
{"req2",{args={a=1,b=2}}}
}
Every request in location.capture_multi is contained within it's own table which can do all of things that
we saw in a simple ngx.location.capture
. Cool isn't it?
Q:Is location.caputure/capture_multi synchronous?
A: Yes. location capture is synchronous but non blocking. Synchronous but non blocking? Well yes. You see the location capture provides a lua interface over what are known as subrequests in nginx world. Quoting from the article linked above:-
Subrequests may look very much like an HTTP request in appearance, their implementation, however, has nothing to do with neither the HTTP protocol nor any kind of socket communication. A subrequest is an abstract invocation for decomposing the task of the main request into smaller "internal requests" that can be served independently by multiple different location blocks, either in series or in parallel.
when the Nginx core processes a subrequest, it just calls a few C functions behind the scene, without doing any kind of network or UNIX domain socket communication. For this reason, subrequests are extremely efficient.
All right we will now try to translate what a trivial location capture might look like if written using nginx configurations. For this we will be making use of the echo module. Consider a single location capture like
local res = ngx.location.capture("/url")
ngx.say(res.body)
initiates a subrequest at nginx's level and it's equivalent to:-
location /main {
echo_location /url;
}
location /url{
echo hello_url;
}
curl http://localhost:8080/main
would return "hello_url" as the response.
Where as location.capture_multi sends out a series of parallel subrequests to location blocks. For example:-
local res1,res2 = ngx.location.capture_multi{{"/url1"},{"url2"}}
ngx.say(res1.body..res2.body)
the above lua code initiates multiple parallel subrequests to locations url1
and url2
. The results are returned after all the requests have been completed. The equivalent nginx code would be
location /main{
echo_location_async /url1;
echo_location_async /url2;
}
location /url1{
echo hello_url1;
}
location /url2{
echo hello_url2;
}
So by "synchronous yet non blocking" we mean that the subrequests are executed independently and concurrently. Yet the location.capture does not return until all the subrequests have been completed. In case you have multiple subrequests using location.capture_multi the time taken to serve all the requests will be equal the time taken by the longest request.
The ngx.location.capture() can also be interpreted as location.capture_multi{} with a single subrequest.
Of course the code above is just a simplistic translation and as we have already seen location capture can go beyond making simple "GET" requests to location blocks. But hopefully the working of location.capture/capture_multi is a bit more clearer.
Q: Can I make external http requests with location capture?
A: You sure can! Yes the subrequests are internal. Yes there is no http involved while calling the subrequests. But the subrequests are executed independently. This means that even though the subrequest is not dealing with http the location block themselves can make any kind of request that they want. Like we saw in the examples above the following will absolutely work:-
location /google{
resolver <IP_ADDRESS>;
proxy_pass http://www.google.com/;
}
local res = ngx.location.capture("/google")
-- the res.body should contain the html source of google.com
ngx api exposes a req object that allows us to configure explicitly our req parameters before making a request to the server. This means that you can easily add/remove http headers, body, configure the method of the req etc
On the other hand the ngx api also provides ability to read an incoming req from the client. Which means that you can read the http headers and the body check the method of the req and then create an appropriate response.
Quite naturally the req table forms a very important part of ngx api. Let us take a deeper look at it then:-
The headers of the request
ngx.req.get_headers()
gives you back a table of headers for an incoming request
which you can then query as usual. For example
local headers = ngx.req.get_headers()
local cookie = headers["Cookie"]
local etag = headers["Etag"]
local host = headers["Host"]
Similarly it's counter-part ngx.req.set_header
is used to set a req header for outgoing requests
ngx.req.set_header("Content-type","application/json")
If for some reason you want the actual headers sent by the client instead of the ones that have been parsed
into a table you can use ngx.req.raw_header
To clear a header you can use ngx.req.clear_header
. Can be very useful when making
a proxy req from the client to the server.
The body of the request
ngx provides ngx.req.read_body()
to read all of the body data. Once the body has been read a convenience method
is provided in ngx.req.get_body_data
and ngx.req.get_post_args
which return a string/lua table.
ngx.req.read_body()
local args = ngx.req.get_post_args()
-- just like the headers, args is a lua table.
To set the body data you can use ngx.set_body_data
The method of the request
ngx.req.get_method
is used to get the method of an incoming request. ngx.req.set_method
is used to set
the method for an outgoing request.
local method = ngx.req.get_method
ngx.req.set_method(ngx.HTTP_POST)
The uri of the request
ngx lua also allows you to change the uri from which the req was initiated by the client.
ngx.req.set_uri("/foo")
Additionally the set_uri takes in a optional boolean parameter that tells nginx to keep searching for newly set uri location
ngx.req.set_uri("/foo",true)
The context of execution becomes important in this case since it mimics the behaviour of nginx rewrite directive. Thus the only allowed context where the optional bool parameter can be used as true is the rewrite_by_lua/rewrite_by_lua_file
The url arguments
You can also modify the query string parameters of the req. Here's how:
ngx.req.set_uri_args("a=3&b=hello%20world")
ngx.req.set_uri_args({ a = 3, b = {5, 6} })
-- this will be translated as "a=3&b=5&b=6"
ngx lua allows setting the uri parameters as query strings and as lua tables. You can get the url arguments using
ngx.req.get_uri_args()
.
local is_test = ngx.req.get_uri_args()["test"]
The get_uri_args() returns a lua table that can be queried like a normal lua table.
Just like the req the ngx api allows you to modify the response that goes back to client. If
you have read the ngx.location.capture
section you should already be familiar with many
of the functions that allow you to modify the response. Nevertheless we will take a better look here: -
The headers of the response
The ngx api gives us two ways to deal with the headers that will be sent out in the response. First there is the
ngx.header
that contains a list of headers that will be sent out as the response to the current request.
Using ngx.header
you can update an existing header or add new headers. You read from ngx.header
in
exactly the same way as you would read from any other lua table.
local content_type = ngx.header.content_type -- reads "Content-Type header"
Note that any '-' in a header is replaced by a '_'. You can still read the header in the original form like so:-
local content_type_orig = ngx.header["Content-type"]
You can set a value to the header in the same way you set a value to key in a lua table.
ngx.header.content_type = "application/json" -- sets the content type header
In case you want to set multiple values to a header you just have to pass a list of values:-
ngx.header["My-Multi-Value-Header"] = {"1","2"}
Similarly if you read a multivalued header a list will be returned as a response.
local multi_val_read = ngx.header["My-Multi-Value-Header"]
-- multi_val_read = {"1","2"}
One important thing to note here is that if you try to set multiple values to a header that can only contain a single value the last item from the list will be chosen. For instance take the content type header:-
ngx.header.content_type = {'a', 'b'}
would result in
ngx.header.content_type = 'b'
Although an ngx.header looks and behaves like a table it is not.The ngx.header does not return an iteratable lua table. For that purpose use:-
ngx.resp.get_headers()
which is quite similar to ngx.req.get_headers()
.
It simply returns a list of response headers that will be sent to the client. The value returned is a proper lua table which can be iterated upon.
local resp_headers = ngx.resp.get_headers()
Finally if you use the response from ngx.location.capture
the headers are found in res.header
. The result
returned is a proper lua table and can be iterated upon.
The status of the response
The response status can be read and updated using the ngx.status
local status = ngx.status -- read the status of the response being sent
ngx.status = ngx.HTTP_NOT_MODIFIED -- set the status code to 304. Using ngx constant here, but you may supply numerical values
The body of the response
Unlike response headers and response status there is no prepping stage for the response body.That is there is no ngx.res.body()
method where you can set the body before sending the response. Openresty instead offers two methods in ngx.say()
and ngx.print()
any argument to the methods will be joined and sent as the res body. It is also important to note that calling any one of these methods means that the response will be sent back to the client. So the response headers and the response status that you have prepared up to this point will be sent back to the client once you call print() or say().
ngx.print("Hello world") --sends Hello world
ngx.say("Hello world") -- sends Hello world/n that is the body appended with a newline
ngx.say(cjson.encode({a=1,b=2})) -- you can also send json in the response body
Remember our first configuration. Specifically this directive `error_log logs/error.log;. We defined all of the errors to be logged in the ./logs/error.log file. One of the ways to read from the error log is to set a tail on it like so:-
tail -f ./logs/error.log
The -f option is for "following" the error log and outputting the results when new data is added to it.
There is however another method that involves configuring nginx conf file to tell it to automatically output the error to the terminal. Add this line to your configuration file:-
error_log /dev/stderr;
and now all your errors will be logged to the terminal.
But error logs are only effective when an error occurs in our program. What if we want to say quickly see the value of a variable? For these cases we can use a combination of ngx.log()
and the nginx's logging constants to write to the error log. For instance we can do this:-
local body = res.body
ngx.log(ngx.ERR,body)
and it will append the body data to the error log. One of the areas where I feel that openresty is lagging in the code inspection part. Unlike it's name ngx.print()
does not print to the console but instead outputs the data to the client. Although using a combination of displaying the error log on the console and openresty's log functions we can display results on our terminal but still it is not as good as say console.log()
in node js. Maybe the tooling for debugging openresty scripts will improve in the future. But it's a minor impedance that does not bother much once you get used to it.
Recently I came to know about zerobrane studio a lightweight IDE for lua written in lua! Among other things it allows you to debug openresty scripts.
Now I know it's hard to simply switch editors from one project to another but I think it's a worthwhile to try out zerobrane for you openresty project because the default debugging is is a bit hard to get your head around.
What with all the changes one has to do in the config files and all the `ngx.log()` statements one has to write in the code and the all the despair that one has to experience when one learns that `ngx.print()` statement does not actually print, one might give up just because he can't get `ngx.var.test_var` to show up on the screen! Which would be a shame.  It's a heavenly feeling if you thimk about it.
Also when you see the author showing off this live coding video it's hard not to drool a little.
In the previous sections we stressed on using local variables by default and refrain from using global variables in ngx_lua. I will try to expand a bit on the reason for the emphasis.
ngx_lua is designed with the principle of request isolation in mind. According to this principle every request handler is run in it's own co routine with it's own environment. Two requests handlers do not share data between them and are executed independently of one another. It follows therefore that the global variables that are defined in the context of one request handler will not be accessible from another request handler. And if you try to access what you think is a global variable it will only result in nil errors.
But what about modules?
Openresty allows you to use your own modules as well as the modules that come with it. A statement
like local module = require("/dir/packer")
will load the package "packer" in the local variable "module". But what if we require this module again in another
handler would lua reload the package again? No it wouldn't. That is because after package is loaded it is
cached inside a package.loaded
table in lua. Any subsequent requests for that module will
simply read from the package.loaded table. So there will no performance penalties. Remember though that when you set lua_code_cache
to off the packages are not cached and are reloaded on every request.
True global variables in ngx_lua can be declared in the init_by_lua directive. Any global variable that you declare here will be accessible from all the nginx's request handlers.
So we can combine the facts that lua caches it's modules in a package.loaded
table and
that global variables can be declared in the init_by_lua directive and derive a very useful
conclusion out of it. That any module you load in the init_by_lua will be accessible from
all the request handler without any need of requiring it. So all those modules that
you need across many different request handlers can be declared here. I find that
the cjson
module that come prepackaged with lua to be a good contender for global declaration since
almost all my request handlers use it.
In fact global variables themselves are loaded into the _ENV table. Thus any global variable that you access is a table look up which can be expensive as compared to accessing a local variable. If you want to know why check out this question on stackoverflow.
Openresty comes with cjson pre installed. The usage of cjson in lua is pretty simple. You use
cjson.encode()
to encode the data into json and cjson.decode
to decode it. However lua
has a couple of unique features that might catch you off guard. So we will walk through a
couple of examples to see how lua encodes and decodes json using cjson. And yes there are
other json encoders and decoders but cjson is considered the best among them.
Before we see the actual examples I would suggest that you
install cjson and
inspect. I am using lua 5.2 for this guide and I've
put cjson and inspect in /usr/local/lib/lua/5.2/
directory. Finally you could copy and
paste the code in the lua shell or you can just clone
this file
and execute it (in the terminal type lua followed by file name).
JSON basically consists of only two data structures. An object/hash/dictionary and an array. An object in json will be translated to a table in lua. An array in json will also be translated to a table in lua.... That is because in lua arrays are represented as tables. In fact lua has only one data structure tables. Every other data structure is implemented on the top of a table. For example this is a simple table in lua
local simple_table = {
des = "I am a simple table"
}
This seems pretty close to what a json object looks like right? To access the properties of this table
you do simple_table.des
or simple_table["des"]
. Now here is what an array looks like in lua:-
local simple_arr = {"three","little","pigs"}
just like a table except without the keys. In array the items are indexed by numbers starting from 1 (more on this below) so you access an array just like you access a table except with numbers instead of named keys. So a simple_arr[1] will give "three" as the output. simple_arr[2] will give "little" as output and so on.
Let us now inspect how a complex json object is decoded
//json object
{
"admins": {
"names": ["superuser"],
"roles": ["admins"]
},
"members": {
"names": ["user1","user2"],
"roles": ["developers"]
}
}
When decoded using cjson.decode()
it would be converted into the following lua table.
{
admins = {
names = { "superuser" },
roles = { "admins" }
},
members = {
names = { "user1", "user2" },
roles = { "developers" }
}
}
as you can see the arrays are converted to lua tables. So how do we access the properties of this table?
Suppose we want to access the members of the decoded json. It is quite simple
local members = json_decoded.members
How about the names of the members?
local user1 = members.names[1]
Note the index of the array. It is 1. But shouldn't it be zero? Well in lua the arrays are indexed starting from 1. So for example:-
local lua_arr = {'one','two','three'}
lua_arr[1] -- one
lua_arr[2] --two
lua_arr[3] -- three
this might be confusing at first as it is different from almost every other programming language but once you are aware of this fact it won't cause any other problems. cjson decodes null in a json to a lua lightuserdata and gives you cjson.null for comparison.
This should help you deal with any kind of json you come across in lua. If you encounter any problems refer to the cjson manual it is quite small and easy to understand.
When you are building openresty applications you are usually working either with the nginx configuration files or with the lua code in various *by_lua_directives. Therefore structuring openresty applications involves organizing the nginx's configuration files and organizing the lua code. I have not come upon any guideline that lays down the principle for doing this so I am just going to write down how I structure my code for openresty applications. Maybe that will help you....
nginx configuration files
I always split my nginx configuration files on the server block. One portion of the file contains a set of global directives that will be used by all the blocks. This is the main file. The other file then contains location blocks for different server blocks and they are all included in the main configuration by the include directive. For example here is what my main configuration file looks like"-
worker_processes 1;
error_log /dev/stderr;
#nginx -p ./ -c ./wrinq.ngx.conf
events {
worker_connections 1024;
}
http {
lua_code_cache off; #only during development
init_by_lua 'cjson = require("cjson")';
include mime.types;
root ./;
server {
server_name localhost;
listen 3125;
include ./routes.conf;
}
}
Here is a portion of "routes.conf"
location /couchdb{
proxy_pass http://<IP_ADDRESS>:5984/$arg_endpoint?$args;
}
location /paypal_adaptive_api{
resolver <IP_ADDRESS>;
proxy_pass https://svcs.sandbox.paypal.com/AdaptivePayments/Pay;
}
To nginx it makes no difference (for it it's all one big config file) but it helps me focus when I separate out the configuration for location blocks from global configuration. Global configuration has hardly changed since I first committed it but I regularly update the "routes.conf" file. The benefit of splitting on location is that in one quick glance of your main file you can tell how many servers you are using, what their addresses are, what configuration they are working with. And this is very useful to me since in one configuration I usually define multiple server blocks depending upon the requirements. The main configuration file thus serves as a "table of contents" for my application.
The lua code
The organization of lua code is mostly similar to what you would do in any lua application. You create lua files. For repeat behaviour you extract out the functions into modules. Use those modules in your files by requiring them. Basic stuff.
Openresty however allows you to add lua scripts as strings. Do not do this. It makes things impossibly hard to debug. Plus writing code becomes a chore as the text editor refuses to analyze the code does not provide any code completion etc. Lots of inconvenience that can be avoided simply by using *by_lua_file directives.
As to the directory structure. I like to use the one that I defined in the beginning of the book.
logs folder- for the nginx's logs
lua folder - for the lua code
and all the configuration files in the parent directory. Simple and straightforward.
But if you have a preferred way of organizing your code feel free to do it that way. All that matters is that when you read your code it should be easy for you to understand it. Apart from that use whatever that suits you.
[generate barebones openresty applications from the command line]
Openresty supports concurrency on two different levels. On nginx level and on lua level. We'll talk about both. Let's start with nginx.
When nginx starts it's execution it spawns a master process. This master process is responsible
for reading, checking, loading configuration files and communicating with worker processes.
All of the actual work is done by the worker processes. The number of worker process is specified in
the configuration file by the worker_processes
directive. Generally one worker process
per cpu core is used. So far so good.
The handling of requests by the worker processes is much more interesting. Each worker process runs in a single thread of execution "reacting" to the signals from the master. Events coming on the master process are passed along to the worker processes or stored in a queue from where it is read by a worker process. The worker processes handle these events but they don't wait around for them to be completed. When they are notified of the completion they send back the response. This is what it looks like inside nginx
An event happens ----> worker sends the request to be processed in a handler -----> worker starts servicing other events -----> on completion of event it sends back the response.
Concurrency in lua is implemented with the help of coroutines. A coroutine is a lightweight(green) thread that is spawned from the lua's execution environment. It is important to note that a coroutine is not an os level thread. It is a separate process with it's own stack and a line of execution that is runs in collaboration with lua's execution loop.
A good explanation of what problems coroutines solve is given in this stackoverflow answer
"The problem coroutines resolve is 'I want to have a function I can execute for a while, then go back to do other thing, and then come back and have the same state I had when I left it'.
"Notice that I didn't say 'I want it to keep running while I do other things"; the flow of code "stops" on the coroutine, and only continues on it when you go back to it.'"
At any time you can only have one coroutine running but that coroutine can suspend it's execution and resume it from the point where it left.
"Synchronous yet non blocking everywhere--or how event loops and coroutines come together in openresty"
One of things about lua's coroutines is that the responsibility of scheduling them lies upon the programmer. That is to say they are not scheduled by the language run time. Openresty introduces a concept of light threads which are just lua's coroutines scheduled by the ngx_lua environment.
Openresty's directives-- content_by_lua,rewrite_by_lua,access_by_lua are run independently in their own light threads. Scheduling and other thread level management tasks are handled by ngx_lua run time on your behalf. You don't have to write any code for spawning or aborting the execution thread. So when in a location block you do:
location /some_location {
content_by_lua 'ngx.say("hello")';
}
The code inside the content_by_lua directive handler is run in it's own thread. That thread is automatically created for you by the ngx_lua run time.
Not all directives are run in this way though. One example of this is the set_by_lua directive which is blocking. You can do a quick check in the official openresty manual to see which directives are run in their own coroutines and which are not.
Since most of the "heavy" code for an openresty application is written within these three directives(access,content and rewrite) majority of your application would be concurrent by default. You've just got to be careful around those directives that are blocking.
The luaJIT VM
There is one more level of concurrency introduced by openresty that we should discuss here. This works on the luaJIT VM level. All the lua code in openresty is executed by luaJIT. Just in case you didn't know LuaJIT is a just in time compiler for lua renowned for it's speed and efficiency. Openresty spawns a sandboxed luaJIT execution environment for every nginx worker process.
Since we usually keep a worker thread per cpu core. So this more or less means one luaJIT VM per cpu core. Now we have everything we need to understand the concurrency model of openresty.
All in all it is very difficult to write blocking code in openresty :)
The resty cli allows you to run your openresty scripts from the terminal like you'd run your normal lua scripts.
The resty cli comes packaged with openresty. You don't need to install it separately.
It is present in the bin sub directory of the openresty install path. On my machine
resty was at
/usr/local/openresty/bin
So if you export this path with
export PATH=/usr/local/openresty/bin:$PATH
you can invoke
the resty client directly from the terminal like so:-
which resty
/usr/local/openresty/bin
"What can I do with resty cli?"
You can use resty cli as a test runner for your openresty scripts. Or maybe build command line utilities. Personally I use it to run tests. The usage of resty cli is pretty simple and straightforward. You can either pass a lua string to be executed or the name of the lua file as an argument to the resty command.
resty -e "ngx.say('hello world')"
resty hello_world.lua
"How does it work"
The resty cli is simply a perl program. It works by creating a temporary directory which mimics the usual nginx directory structure. Within this directory you have your configuration files, a logs directory and other sub-directories and files that are common in any nginx application.
The config file contains the rules of what all is allowed to be executed by the cli and what all is not. Here's what it looks like:-
error_log /dev/stderr warn;
#error_log /dev/stderr debug;
events {
worker_connections $conns;
}
http {
access_log off;
lua_socket_log_errors off;
resolver @nameservers;
$lua_package_path_config
init_by_lua '
--lua code to initlize the global lua VM ';
init_worker_by_lua '
$loader -- perl variable for loading lua files
-- rest of lua code to execute the loaded file
-- in a timer.
';
}
All the code that is passed to the resty client is run against this configuration file. One thing that becomes quite apparent at first glance is that there is no server block in the config. This means that you can't have a headless nginx instance that listens to incoming connections. The error_log directive prints all the errors on the terminal instead of the error file.
The init_by_lua directive prepares the resty client for the execution of the inputs that are given from the terminal. Once the initialization is complete the actual execution is handed off to init_worker_by_lua.
The $loader
is just a perl function that reads the lua code that
is input either as a string or as a file. It passes on this code to an nginx timer function
where the code is compiled and executed in a loadstring.
local ok, err = ngx.timer.at(0, function ()
-- io.stderr:write("timer firing")
local ok, err = pcall(gen)
-- the $loader function loads the compiled code in a gen variable
if not ok then
return handle_err(err)
end
local rc = err
if rc and type(rc) ~= "number" then
return handle_err("bad return value of type " .. type(rc))
end
return exit(rc)
end)
if not ok then
return handle_err(err)
end
-- print("timer created")
end)
if not ok then
return handle_err(err)
end
Since everything is executed in the context of an ngx.timer.at you can only use the api that is available within ngx.timer.at This means that you can't use ngx.location.capture with resty client, which is logical since the nginx config file is not listening to any incoming connections due to the absence of the server block but .... :( At least you've still got a good chunk of api available to play with.
"But how come ngx.print and ngx.say work?"
Puzzled me too. ngx.say() and ngx.print() are a part of downstream api that sends back the http response to the client. Both ngx.print and ngx.say are disabled in the context of ngx.timer.at which means that you shouldn't be able to use it right? Right. So then what gives?
local function output(...)
local args = {...}
return stdout:write(expand_table(args, true))
end
ngx.print = output
ngx.say = function (...)
local ok, err = output(...)
if ok then
return output("\\\\n")
end
return ok, err
end
print = ngx.say
Well it seems that ngx.print and ngx.say functions are overwritten to display the output on the terminal instead of sending back an http response. Sort of an anticlimax in my opinion I expected something more exotic.
-Akshat Jiwan Sharma


Cassandra
Table of Contents
- Overview
- Setup - The basics of getting started with Cassandra
- Usage - Configuration options and additional functionality
- Reference
- Limitations - OS compatibility, etc.
- Development
Overview
A Puppet module to install and manage Cassandra, DataStax Agent & OpsCenter
Setup
What Cassandra affects
What the Cassandra class affects
- Installs the Cassandra package (default cassandra22 on Red Hat and cassandra on Debian).
- Configures settings in
${config_path}/cassandra.yaml
. - On CentOS 7 if the
init
service provider is used, then cassandra is added as a system service. - Optionally ensures that the Cassandra service is enabled and running.
- On Debian systems:
- Optionally replace
/etc/init.d/cassandra
with a workaround for CASSANDRA-9822.
- Optionally replace
What the cassandra::datastax_agent class affects
- Optionally installs the DataStax agent.
- Optionally sets JAVA_HOME in /etc/default/datastax-agent.
What the cassandra::datastax_repo class affects
- Optionally configures a Yum repository to install the Cassandra packages from (on Red Hat).
- Optionally configures an Apt repository to install the Cassandra packages from (on Debian).
What the cassandra::dse class affects
- Optionally configures files in the
/etc/dse
directory if one is using DataStax Enterprise.
What the cassandra::firewall_ports class affects
- Optionally configures the firewall for the Cassandra related network ports.
What the cassandra::java class affects
- Optionally installs a JRE/JDK package (e.g. java-1.7.0-openjdk) and the Java Native Access (JNA).
What the cassandra::optutils class affects
- Optionally installs the Cassandra support tools (e.g. cassandra22-tools).
Upgrading
We follow SemVer Versioning and an update of the major release (i.e. from 1.Y.Z to 2.Y.Z) will indicate a significant change to the API which will most probably require a change to your manifest.
Changes in 2.0.0
This is a major change to the API and you will more than likely need to change your manifest to accomodate these changes.
The service_ensure
attribute of the cassandra class now defaults to
undef, users who do want to manage service status in Puppet can still set
it to true. If leaving the value at the default and setting
service_refresh
and service_enable
to false will mean that the
user and not Puppet running will control the running state of
Cassandra. This currently works OK on the Red Hat family, but
has issues on Debian due to
CASSANDRA-2356
during an initial install or package upgrade.
All the functionality relating to OpsCenter has been divested to the locp/opscenter module on Puppet Forge.
It should also be noted that the module no longer creates directories for
the data
, commitlog
, saved_caches
and for Cassandra 3 the hints
directory. These resources will now need to be defined in your
manifest/profile.
For a list of features that have been deprecated in this release, please see https://github.com/voxpupuli/puppet-cassandra/wiki/Deprecations
For details on migrating from the version 1.X.X attributes to the settings
hash, see
(https://github.com/voxpupuli/puppet-cassandra/wiki/Suggested-Baseline-Settings)
Please also see the notes for 2.0.0 in the CHANGELOG.
Changes in 1.19.0
The hints_directory documentation will cause a change in the cassandra.yaml file regardless of the value you set it to. If you do not wish this to result in a refesh of the Cassandra service, please set service_refresh to false.
Changes in 1.9.2
Now that Cassandra 3 is available from the DataStax repositories, there is a problem (especially on Debian) with the operating system package manager attempting to install Cassandra 3. This can be mitigated against using something similar to the code in this modules acceptance test. Please note that the default Cassandra package name has now been changed from 'dsc'. See the documentation for cassandra::package_name below for details.
if $::osfamily == 'RedHat' {
$version = '2.2.4-1'
} else {
$version = '2.2.4'
}
class { 'cassandra':
package_ensure => $version,
}
Changes in 1.8.0
A somewhat embarrassing correction to the spelling of the cassandra::fail_on_non_suppoted_os to cassandra::fail_on_non_supported_os.
Issues when Upgrading to 1.4.0
Unfortunately both releases 1.3.7 and 1.4.0 have subsequently been found to call a refresh service even when no changes had been made to the underlying configuration. In release 1.8.0 (somewhat belatedly) the service_refresh flag has been introduced to mitigate against similar problems.
Issues When Upgrading to 1.3.7
- Please see the notes for 1.4.0.
Changes in 1.0.0
- cassandra::cassandra_package_ensure has been renamed to cassandra::package_ensure.
- cassandra::cassandra_package_name has been renamed to cassandra::package_name.
Changes in 0.4.0
There is now a cassandra::datastax_agent class, therefore:
- cassandra::datastax_agent_package_ensure has now been replaced with cassandra::datastax_agent::package_ensure.
- cassandra::datastax_agent_service_enable has now been replaced with cassandra::datastax_agent::service_enable.
- cassandra::datastax_agent_service_ensure has now been replaced with cassandra::datastax_agent::service_ensure.
- cassandra::datastax_agent_package_name has now been replaced with cassandra::datastax_agent::package_name.
- cassandra::datastax_agent_service_name has now been replaced with cassandra::datastax_agent::service_name.
Likewise now there is a new class for handling the installation of Java:
- cassandra::java_package_ensure has now been replaced with cassandra::java::ensure.
- cassandra::java_package_name has now been replaced with cassandra::java::package_name.
Also there is now a class for installing the optional utilities:
- cassandra::cassandra_opt_package_ensure has now been replaced with cassandra::optutils:ensure.
- cassandra::cassandra_opt_package_name has now been replaced with cassandra::optutils:package_name.
Changes in 0.3.0
cassandra_opt_package_ensure changed from 'present' to undef.
The manage_service option has been replaced with service_enable and service_ensure.
Beginning with Cassandra
Create a Cassandra 2.X cluster called MyCassandraCluster which uses the GossipingPropertyFileSnitch and password authentication. In this very basic example the node itself becomes a seed for the cluster and the credentials will default to a user called cassandra with a password called of cassandra.
# Cassandra pre-requisites
include cassandra::datastax_repo
include cassandra::java
class { 'cassandra':
settings => {
'authenticator' => 'PasswordAuthenticator',
'cluster_name' => 'MyCassandraCluster',
'commitlog_directory' => '/var/lib/cassandra/commitlog',
'commitlog_sync' => 'periodic',
'commitlog_sync_period_in_ms' => 10000,
'data_file_directories' => ['/var/lib/cassandra/data'],
'endpoint_snitch' => 'GossipingPropertyFileSnitch',
'listen_address' => $::ipaddress,
'partitioner' => 'org.apache.cassandra.dht.Murmur3Partitioner',
'saved_caches_directory' => '/var/lib/cassandra/saved_caches',
'seed_provider' => [
{
'class_name' => 'org.apache.cassandra.locator.SimpleSeedProvider',
'parameters' => [
{
'seeds' => $::ipaddress,
},
],
},
],
'start_native_transport' => true,
},
require => Class['cassandra::datastax_repo', 'cassandra::java'],
}
However, PLEASE note that this is the ABSOLUTE MINIMUM configuration to get Cassandra up and running but will probably give you a rather badly configured node. Please see Suggested Baseline Settings for details on making your configuration a lot more robust.
For this code to run with version 3.X of Cassandra, the hints_directory
will
also need to be specified:
...
class { 'cassandra':
settings => {
...
'hints_directory' => '/var/lib/cassandra/hints',
...
},
require => Class['cassandra::datastax_repo', 'cassandra::java'],
}
Hiera
In your top level node classification (usually common.yaml
), add the
settings hash and all the tweaks you want all the clusters to use:
cassandra::baseline_settings:
authenticator: AllowAllAuthenticator
authorizer: AllowAllAuthorizer
auto_bootstrap: true
auto_snapshot: true
...
Then, in the individual node classification add the parts which define the cluster:
cassandra::settings:
cluster_name: developer playground cassandra cluster
cassandra::dc: Onsite1
cassandra::rack: RAC1
cassandra::package_ensure: 3.0.5-1
cassandra::package_name: cassandra30
Usage
Setup a keyspace and users
We assume that authentication has been enabled for the cassandra cluster and we are connecting with the default user name and password ('cassandra/cassandra').
In this example, we create a keyspace (mykeyspace) with a table called 'users' and an index called 'users_lname_idx'.
We also add three users (to Cassandra, not the mykeyspace.users table) called spillman, akers and boone while ensuring that a user called lucan is absent.
class { 'cassandra':
...
}
class { 'cassandra::schema':
cqlsh_password => 'cassandra',
cqlsh_user => 'cassandra',
cqlsh_host => $::ipaddress,
indexes => {
'users_lname_idx' => {
table => 'users',
keys => 'lname',
keyspace => 'mykeyspace',
},
},
keyspaces => {
'mykeyspace' => {
durable_writes => false,
replication_map => {
keyspace_class => 'SimpleStrategy',
replication_factor => 1,
},
}
},
permissions => {
'Grant select permissions to spillman to all keyspaces' => {
permission_name => 'SELECT',
user_name => 'spillman',
},
'Grant modify to to keyspace mykeyspace to akers' => {
keyspace_name => 'mykeyspace',
permission_name => 'MODIFY',
user_name => 'akers',
},
'Grant alter permissions to mykeyspace to boone' => {
keyspace_name => 'mykeyspace',
permission_name => 'ALTER',
user_name => 'boone',
},
'Grant ALL permissions to mykeyspace.users to gbennet' => {
keyspace_name => 'mykeyspace',
permission_name => 'ALTER',
table_name => 'users',
user_name => 'gbennet',
},
},
tables => {
'users' => {
columns => {
user_id => 'int',
fname => 'text',
lname => 'text',
'PRIMARY KEY' => '(user_id)',
},
keyspace => 'mykeyspace',
},
},
users => {
'spillman' => {
password => 'Niner27',
},
'akers' => {
password => 'Niner2',
superuser => true,
},
'boone' => {
password => 'Niner75',
},
'gbennet' => {
'password' => 'foobar',
},
'lucan' => {
'ensure' => absent
},
},
}
Create a Cluster in a Single Data Center
In the DataStax documentation Initializing a multiple node cluster (single data center) http://docs.datastax.com/en/cassandra/2.2/cassandra/initialize/initSingleDS.html there is a basic example of a six node cluster with two seeds to be created in a single data center spanning two racks. The nodes in the cluster are:
| Node Name | IP Address |
|---|---|
| node0 (seed 1) | <IP_ADDRESS> |
| node1 | <IP_ADDRESS> |
| node2 | <IP_ADDRESS> |
| node3 (seed 2) | <IP_ADDRESS> |
| node4 | <IP_ADDRESS> |
| node5 | <IP_ADDRESS> |
Each node is configured to use the GossipingPropertyFileSnitch and 256 virtual nodes (vnodes). The name of the cluster is MyCassandraCluster. Also, while building the initial cluster, we are setting the auto_bootstrap to false.
In this initial example, we are going to expand the example by:
- Ensuring that the software is installed via the DataStax Community
repository by including
cassandra::datastax_repo
. This needs to be executed before the Cassandra package is installed. - That a suitable Java Runtime environment (JRE) is installed with Java Native
Access (JNA) by including
cassandra::java
. This need to be executed before the Cassandra service is started.
node /^node\d+$/ {
class { 'cassandra::datastax_repo':
before => Class['cassandra']
}
class { 'cassandra::java':
before => Class['cassandra']
}
class { 'cassandra':
settings => {
'authenticator' => 'AllowAllAuthenticator',
'auto_bootstrap' => false,
'cluster_name' => 'MyCassandraCluster',
'commitlog_directory' => '/var/lib/cassandra/commitlog',
'commitlog_sync' => 'periodic',
'commitlog_sync_period_in_ms' => 10000,
'data_file_directories' => ['/var/lib/cassandra/data'],
'endpoint_snitch' => 'GossipingPropertyFileSnitch',
'hints_directory' => '/var/lib/cassandra/hints',
'listen_interface' => 'eth1',
'num_tokens' => 256,
'partitioner' => 'org.apache.cassandra.dht.Murmur3Partitioner',
'saved_caches_directory' => '/var/lib/cassandra/saved_caches',
'seed_provider' => [
{
'class_name' => 'org.apache.cassandra.locator.SimpleSeedProvider',
'parameters' => [
{
'seeds' => '<IP_ADDRESS>,<IP_ADDRESS>',
},
],
},
],
'start_native_transport' => true,
},
}
}
The default value for the num_tokens is already 256, but it is included in the example for clarity. Do not forget to either set auto_bootstrap to true or not set the attribute at all after initializing the cluster.
Create a Cluster in Multiple Data Centers
To continue with the examples provided by DataStax, we look at the example for a cluster across multiple data centers http://docs.datastax.com/en/cassandra/2.2/cassandra/initialize/initMultipleDS.html.
| Node Name | IP Address | Data Center | Rack |
|---|---|---|---|
| node0 (seed 1) | <IP_ADDRESS> | DC1 | RAC1 |
| node1 | <IP_ADDRESS> | DC1 | RAC1 |
| node2 | <IP_ADDRESS> | DC1 | RAC1 |
| node3 (seed 2) | <IP_ADDRESS> | DC2 | RAC1 |
| node4 | <IP_ADDRESS> | DC2 | RAC1 |
| node5 | <IP_ADDRESS> | DC2 | RAC1 |
For the sake of simplicity, we will confine this example to the nodes:
node /^node[012]$/ {
class { 'cassandra':
dc => 'DC1',
settings => {
'authenticator' => 'AllowAllAuthenticator',
'auto_bootstrap' => false,
'cluster_name' => 'MyCassandraCluster',
'commitlog_directory' => '/var/lib/cassandra/commitlog',
'commitlog_sync' => 'periodic',
'commitlog_sync_period_in_ms' => 10000,
'data_file_directories' => ['/var/lib/cassandra/data'],
'endpoint_snitch' => 'GossipingPropertyFileSnitch',
'hints_directory' => '/var/lib/cassandra/hints',
'listen_interface' => 'eth1',
'num_tokens' => 256,
'partitioner' => 'org.apache.cassandra.dht.Murmur3Partitioner',
'saved_caches_directory' => '/var/lib/cassandra/saved_caches',
'seed_provider' => [
{
'class_name' => 'org.apache.cassandra.locator.SimpleSeedProvider',
'parameters' => [
{
'seeds' => '<IP_ADDRESS>,<IP_ADDRESS>',
},
],
},
],
'start_native_transport' => true,
},
}
}
node /^node[345]$/ {
class { 'cassandra':
dc => 'DC2',
settings => {
'authenticator' => 'AllowAllAuthenticator',
'auto_bootstrap' => false,
'cluster_name' => 'MyCassandraCluster',
'commitlog_directory' => '/var/lib/cassandra/commitlog',
'commitlog_sync' => 'periodic',
'commitlog_sync_period_in_ms' => 10000,
'data_file_directories' => ['/var/lib/cassandra/data'],
'endpoint_snitch' => 'GossipingPropertyFileSnitch',
'hints_directory' => '/var/lib/cassandra/hints',
'listen_interface' => 'eth1',
'num_tokens' => 256,
'partitioner' => 'org.apache.cassandra.dht.Murmur3Partitioner',
'saved_caches_directory' => '/var/lib/cassandra/saved_caches',
'seed_provider' => [
{
'class_name' => 'org.apache.cassandra.locator.SimpleSeedProvider',
'parameters' => [
{
'seeds' => '<IP_ADDRESS>,<IP_ADDRESS>',
},
],
},
],
'start_native_transport' => true,
},
}
}
We don't need to specify the rack name (with the rack attribute) as RAC1 is the default value. Again, do not forget to either set auto_bootstrap to true or not set the attribute at all after initializing the cluster.
DataStax Enterprise
After configuring the relevant repository, the following snippet works on CentOS 7 to install DSE Cassandra 4.7.0, set the HADOOP_LOG_DIR, set the DSE_HOME and configure DataStax Enterprise to use LDAP for authentication:
class { 'cassandra::datastax_repo':
descr => 'DataStax Repo for DataStax Enterprise',
pkg_url =><EMAIL_ADDRESS>before => Class['cassandra'],
}
class { 'cassandra':
cluster_name => 'MyCassandraCluster',
config_path => '/etc/dse/cassandra',
package_ensure => '4.7.0-1',
package_name => 'dse-full',
service_name => 'dse',
...
}
class { 'cassandra::dse':
file_lines => {
'Set HADOOP_LOG_DIR directory' => {
ensure => present,
path => '/etc/dse/dse-env.sh',
line => 'export HADOOP_LOG_DIR=/var/log/hadoop',
match => '^# export HADOOP_LOG_DIR=<log_dir>',
},
'Set DSE_HOME' => {
ensure => present,
path => '/etc/dse/dse-env.sh',
line => 'export DSE_HOME=/usr/share/dse',
match => '^#export DSE_HOME',
},
},
settings => {
ldap_options => {
server_host => localhost,
server_port => 389,
search_dn => 'cn=Admin',
search_password => secret,
use_ssl => false,
use_tls => false,
truststore_type => jks,
user_search_base => 'ou=users,dc=example,dc=com',
user_search_filter => '(uid={0})',
credentials_validity_in_ms => 0,
connection_pool => {
max_active => 8,
max_idle => 8,
}
}
}
}
Apache Cassandra
DataStax announced in late October 2016 that it was no longer supporting
the community edition of Cassandra or DSC as it was known (see
[Take a bow Planet
Cassandra](http://www.datastax.com/2016/10/take-a-bow-planet-cassandra)
for details). However, the following snippet of code running on Ubuntu
14.04 worked fine without having to change any of the ::cassandra
class
settings:
require cassandra::java
include cassandra::optutils
class { 'cassandra::apache_repo':
release => '310x',
before => Class['cassandra', 'cassandra::optutils'],
}
class { 'cassandra':
...
}
Reference
The reference documentation is generated using the puppet-strings tool. To see all of it, please go to http://voxpupuli.github.io/puppet-cassandra.
Limitations
- When using a Ruby version before 1.9.0, the contents of the Cassandra configuration file may change order of elements due to a problem with to_yaml in earlier versions of Ruby.
- When creating key spaces, indexes, cql_types and users the settings will only be used to create a new resource if it does not currently exist. If a change is made to the Puppet manifest but the resource already exits, this change will not be reflected.
- At the moment the
cassandra::system::transparent_hugepage
does not persist between reboots. - Acceptance for Debian 7 are confined to Cassandra 2.1 and 2.2. There is a conflict between the GLIBC on Debian 7 and the newer releases of Cassandra 3.X.
Development
Contributions will be gratefully accepted. Please go to the project page, fork the project, make your changes locally and then raise a pull request. Details on how to do this are available at https://guides.github.com/activities/contributing-to-open-source.
Please also see the CONTRIBUTING.md page for project specific requirements.
Additional Contributers
For a list of contributers see CONTRIBUTING.md and https://github.com/voxpupuli/puppet-cassandra/graphs/contributors


Apply it on the mixture, reconstruct and listen to the result
Introduction: filtering
Introduction: filtering
Introduction: filtering
Introduction: filtering
Introduction: filtering
Introduction: filtering
Introduction: a brief history
The big picture
Rafii, Zafar, et al. "An Overview of Lead and Accompaniment Separation in Music."
IEEE/ACM Transactions on Audio, Speech and Language Processing (TASLP) 26.8 (2018): 1307-1335.
A brief history: model-driven methods
Harmonicity for the lead
Pitch detection
Clean voices
"Metallic" artifacts
A brief history: model-driven methods
Redundancy for the accompaniment: NMF
Spectral templates
Low-rank assumptions
Bad generalization
A brief history: model-driven methods
Redundancy for the accompaniment: RPCA
Low-rank for music
Vocals as unstructured
Strong interferences in general
A brief history: model-driven methods
Redundancy for the accompaniment: REPET
Repetitive music
Non-repetitive vocals
Solos in vocals
A brief history: model-driven methods
Modeling both lead and accompaniment: source filter
SDR: Source to distortion ratio. Error in the estimate.
SIR: Source to interference ratio. Presence of other sources.
SAR: Source to artifacts ratio. Amount of artificial noise.
E. Vincent et al. "Performance measurement in blind audio source separation."
IEEE transactions on audio, speech, and language processing 14.4 (2006): 1462-1469.
museval (BSSeval v4)
Better matching filters computed track-wise
Faster 10x
F. Stöter et al. "The 2018 Signal Separation Evaluation Campaign."
LVA/ICA 2018.
Introduction: evaluating quality
Hands-on
Loop over some musdb tracks
Evaluate our separation system on musdb
Compare to state of the art (SiSEC18)
Introduction
Deep neural networks
Y. LeCun, et al. "Deep learning". nature, 521(7553), 436 (2015).
 JMLR. (2014) 15(1), 1929-1958.
Training: regularization with dropout
Regularization makes things worse
Training: sampling strategy
Non unique tracks in batch
Not all samples per epoch
Training: sampling strategy
Unique tracks in batch
Not all samples per epoch
Training: sampling strategy
Non unique tracks in batch
All samples per epoch
Training: sampling strategy
Unique tracks in batch
All samples per epoch
Training: sampling strategy
Hands on hierarchical sampling
Implement the 4 strategies with pescador
Apply they on spectrograms
Training: sampling strategy
Unique tracks per batch is slower
All samples per epoch is faster
B. Recht. "Beneath the valley of the noncommutative arithmetic-geometric mean inequality: conjectures, case-studies. and consequences." (2012). Technical report.
Training: data augmentation
Basic augmentation: overlap samples within each track
There are more advanced strategies
S. Uhlich, et al. "Improving music source separation based on deep neural networks through data augmentation and network blending." (2017) ICASSP.
Training: data augmentation
Basic augmentation helps a bit (0.5dB)
Not shown: new tracks are better!
Open Source Unmix (OSU) models
Outline
Testing
Representation
Mono filter tricks
Multichannel Gaussian model
The multichannel Wiener filter
Testing: evaluation
Testing: representations
The first source of poor results: inverse STFT!
Verify perfect reconstruction
Better: use established libraries, like librosa, scipy...
Testing: mono filter tricks
Logit filters
If the mask is 0.8... just put 1
If the mask is 0.2... just put 0
Cheap interference reduction
Multichannel Gaussian model
Multichannel Gaussian model
Multichannel Gaussian model
Multichannel Gaussian model
Multichannel Gaussian model
Multichannel Gaussian model
Testing: the multichannel Wiener filter
Sources and mixtures are jointly Gaussian
We observe the mix, what can we say about the sources?
Testing: the multichannel Wiener filter
Testing: the multichannel Wiener filter
Testing: the multichannel Wiener filter
Testing: the multichannel Wiener filter
Testing: the multichannel Wiener filter
Testing: the multichannel Wiener filter
Testing: the Expectation-Maximization algorithm
Testing: evaluation
Testing: evaluation
Iterations improve SIR
$\Rightarrow$ greatly reduces interferences
Iterations worsen SAR
$\Rightarrow$ introduces distortion
logit has good SIR
$\Rightarrow$ cheap interference reduction
Testing: evaluation
Outline
Conclusion
Resulting baseline
What was kept out
What is promising
Ending remarks
Conclusion: the open source unmix
Conclusion: what was kept out
Exotic representations
Alternative structures
The convolutional neural network (CNN)
The U-NET
The MM-densenet
Deep clustering
Generative approaches
Generative adversarial nets
(Variational) auto encoders
Deep clustering
Full grid search over parameters (fund us!)
Advanced data augmentation (naive=+0.3dB SDR)
Conclusion: what is promising
More data
More data
Even more data
Did we mention more data?
New approaches
Structures with more parameters work better...
Better signal processing helps
Engineering
We got 3dB SDR improvement with no publishable contribution
$\Rightarrow$ evaluating the real impact of a contribution is difficult
Conclusion: ending remarks
Convergence of signal processing, probability theory and DL


---
layout: post
title: Ballmer Threatens (Litigation?) Over the Use of Linux
categories: []
tags: []
status: publish
type: post
published: true
meta:
_original_post_id: ''
_original_import_author: John Duprey
author:
---
-
-
jduprey
- November 23rd, 2004
Panasonic braves Linux-inspired wrath of Ballmer | The Register
"
Was this what Microsoft (Steve Ballmer) was growling and threatening about, when he told Asian countries: "nice little Linux OS you have here. Be a pity if someone sued you..." yesterday? Is it this mass market 3G Linux phone range for Japan, from Panasonic and NEC...?"


@Target(value=TYPE) @Retention(value=RUNTIME) @Documented public @interface MessagingGateway
<gateway/>
) as an abstraction over the messaging API. The target
application's business logic may be completely unaware of the Spring Integration
API, with the code interacting only via the interface.
Important: The IntegrationComponentScan
annotation is required along with
Configuration
to scan interfaces annotated with MessagingGateway
, because the
standard ComponentScan
ignores interfaces.
IntegrationComponentScan
| Modifier and Type | Optional Element and Description |
|---|---|
String |
asyncExecutor
|
GatewayHeader[] |
defaultHeaders
Provides custom message headers.
|
String |
defaultPayloadExpression
An expression that will be used to generate the
payload for all methods in the service interface
unless explicitly overridden by a method declaration. |
String |
defaultReplyChannel
Identifies the default channel the gateway proxy will subscribe to, to receive reply
Message s, the payloads of
which will be converted to the return type of the method signature. |
String |
defaultReplyTimeout
Allows to specify how long this gateway will wait for the reply
Message
before returning. |
String |
defaultRequestChannel
Identifies the default channel to which messages will be sent upon invocation
of methods of the gateway proxy.
|
String |
defaultRequestTimeout
Provides the amount of time dispatcher would wait to send a
Message . |
String |
errorChannel
Identifies a channel that error messages will be sent to if a failure occurs in the
gateway's proxy invocation.
|
String |
mapper
An
MethodArgsMessageMapper
to map the method arguments to a Message . |
String |
name
The value may indicate a suggestion for a logical component name,
to be turned into a Spring bean in case of an autodetected component.
|
public abstract String name
public abstract String defaultRequestChannel
public abstract String defaultReplyChannel
Message
s, the payloads of
which will be converted to the return type of the method signature.public abstract String errorChannel
errorChannel
reference is provided, the gateway will
propagate Exception
s to the caller. To completely suppress Exception
s, provide a
reference to the nullChannel
here.public abstract String defaultRequestTimeout
Message
. This
timeout would only apply if there is a potential to block in the send call. For
example if this gateway is hooked up to a QueueChannel
. Value is specified
in milliseconds; it can be a simple long value or a SpEL expression; array variable
#args is available.public abstract String defaultReplyTimeout
Message
before returning. By default it will wait indefinitely. null
is returned if
the gateway times out. Value is specified in milliseconds; it can be a simple long
value or a SpEL expression; array variable #args is available.public abstract String asyncExecutor
Executor
to use for any of the interface methods that have a Future
return type.
This Executor
will only be used for those async methods; the sync methods
will be invoked in the caller's thread.
Use AnnotationConstants.NULL
to specify no async executor - for example
if your downstream flow returns a Future
.public abstract String defaultPayloadExpression
payload
for all methods in the service interface
unless explicitly overridden by a method declaration. Variables include #args
, #methodName
,
#methodString
and #methodObject
;
a bean resolver is also available, enabling expressions like @someBean(#args)
.public abstract GatewayHeader[] defaultHeaders
public abstract String mapper
MethodArgsMessageMapper
to map the method arguments to a Message
. When this
is provided, no payload-expression
s or header
s are allowed; the custom mapper is
responsible for creating the message.


--- layout: page title: About Me description: Who am I? background: '/img/bg-about.jpg' ---
Hi, I am a graduate student pursuing Data Science at New York University with a strong undergraduate background in Mathematics and Computing from Indian Institute of Technology Guwahati, India.
I love to play around with data and extract useful insights from it. Products backed with strong artificial intelligence mesmerise me and I dream to develop products that are capable of reciprocating the same wow factor. I am also passionate about deep learning and am actively working towards increasing my knowledge in the field.


Pagination component enables the user to select a specific page from a range of pages
Import TuiPaginationModule into a module where you want to use our component
TuiPaginationModule
Add to the template:


To set the initial map center and zoom adjust the preview map to the desired area and press the button below to record the extent.
If no extent is recorded for this snapshot, the initial map center and zoom will be that of the previous snapshot/scenario in the workshop. Do not record a map extent if this is the desired effect.
An initial map center and zoom have been set. You can adjust the preview map to the desired area/region and update the recorded extent or clear the recorded extent.
With an extent recorded, the map view will snap to the saved area whenever the user switches to this snapshot.
There are no layers on this snapshot. Click the "+" to add a layer.


You can configure this directive to accept input in any format understood by moment.js, however, when you leave the field it will be displayed in the specified format.
The background of the input box will be red whenever the input is an invalid date.
Input format is 'YYYY/MM/DD' (and ISO 8601, which is always supported).
Input parsing is strict (meaning input must match format exactly).
Display format is YYYY/MM/DD.
Stored as milliseconds: {{ data.date1 }}
Input format is 'YYYY/MM/DD hh:mm A', 'YYYY/MM/DD', or 'MMM DD YYYY' (and ISO 8601, which is always supported).
Input parsing is strict (meaning input must exactly match one of input formats).
Display format is 'YYYY/MM/DD hh:mm A'.
Stored as a date: {{ data.date2 }}
Input format is 'YYYY/MMM/DD hh:mm A' ( note the MMM for month ).
Input parsing is NOT strict (in this case, the moment parser is very, very forgiving - entering only
'1' will likely result in a valid date).
Display format is 'YYYY/MMM/DD hh:mm A'.
Stored as: {{ data.date3 }}
Input format is 'M/D/YYYY h:mm A'.
Display format is 'M/D/YYYY h:mm A'.
Stored as: {{ data.dateDropDown }}
Any invalid dates will have a ng-invalid class assigned by Angular


3 Dev Adam (English: 3 Giant Men) is a 1973 Turkish cult superhero film featuring Aytekin Akkaya as Captain America and Yavuz Selekman as Santo called to Istanbul on a special mission to stop the villainous Spider-Man and his criminal gang. The film, which went on nationwide general release across the country on November 1, 1973, was completely unauthorized by the copyright owners of the characters depicted.


Fairy Tales and Other Traditional Stories
by FCIT
“The Pig Brother”
by Laura Richards
Additional Information
- Year Published: 0
- Language: English
- Country of Origin: United States of America
- Source: Sara Cone Bryant, ed., How to Tell Stories to Children, and Some Stories to Tell
-
Readability:
- Flesch–Kincaid Level: 2.6
- Word Count: 635
- Genre: Fairy Tale/Folk Tale
- Keywords: cleanliness
- ✎ Cite This
Downloads
There was once a child who was untidy. He left his books on the floor, and his muddy shoes on the table; he put his fingers in the jam pots, and spilled ink on his best pinafore; there was really no end to his untidiness.
One day the Tidy Angel came into his nursery.
“This will never do!” said the Angel. “This is really shocking.
“Yes, you have,” said the Angel. “You may not know him, but he will know you. Go out in the garden and watch for him, and he will soon come.”
“I don’t know what you mean!” said the child; but he went out into the garden and waited.
Presently a squirrel came along, whisking his tail.
“Are you my brother?” asked the child.
The squirrel looked him over carefully.
“Well, I should hope not!” he said. “My fur is neat and smooth, my nest is handsomely made, and in perfect order, and my young ones are properly brought up. Why do you insult me by asking such a question?”
He whisked off, and the child waited.
Presently a wren came hopping by.
“Are you my brother?” asked the child.
“No, indeed!” said the wren. “What impertinence! You will find no tidier person than I in the whole garden. Not a feather is out of place, and my eggs are the wonder of all for smoothness and beauty. Brother, indeed!”
By-and-by a large Tommy Cat came along.
“Are you my brother?” asked the child.
“Go and look at yourself in the glass,” said the Tommy Cat haughtily, “and you will have your answer. I have been washing myself in the sun all the morning, while it is clear that no water has come near you for a long time. There are no such creatures as you in my family, I am humbly thankful to say.”
He walked on, waving his tail, and the child waited.

The child did not wish to ask the pig if he were his brother, but the pig did not wait to be asked.
“Hallo, brother!” he grunted.
“I am not your brother!” said the child.
“Oh yes, you are!” said the pig. “I confess I am not proud of you, but there is no mistaking the members of our family. Come along, and have a good roll in the barnyard! There is some lovely black mud there.”
“I don’t like to roll in mud!” said the child.
“Tell that to the hens!” said the Pig Brother. “Look at your hands and your shoes, and your pinafore! Come along, I say! You may have some of the pig-wash for supper, if there is more than I want.”
“I don’t want pig-wash!” said the child; and he began to cry.
Just then the Tidy Angel came out.
“I have set everything to rights,” she said, “and so it must stay.
The Pig Brother grunted.
“Small loss!” he said. “There will be all the more wash for me!” And he trotted off.


Browse a list of suggested Databases for these subjects:
JavaScript Currently Disabled: Sorry, but we are unable to retrieve the list of databases if JavaScript is disabled.