Browse Source

Merge branch 'master' of ssh://git.kea.isc.org/git/kea

# Conflicts:
#	ChangeLog
Tomek Mrugalski 8 years ago
parent
commit
2a89ec50e4

+ 5 - 1
ChangeLog

@@ -1,8 +1,12 @@
-1255.	[bug]		tomek
+1256.	[bug]		tomek
 	Control Agent now writes proper configuration when using
 	config-write command.
 	(Trac #5253, git a1b5da4db6ebfa9635bbe411ec363cdcc4fd1d28)
 
+1255.	[bug]		marcin
+	Fixed failing unit tests in libkea-http.
+	(Trac #5260, git 43394f76efb1634155c04b205dec7361fc21f4f9)
+
 1254.	[func]		tomek
 	Various improvements needed for upcoming host commands library:
 	host data source is now able to delete hosts, hosts can be exported

+ 14 - 4
doc/guide/shell.xml

@@ -25,7 +25,7 @@
     <title>Shell Usage</title>
     <para><command>kea-shell</command> is run as follows:
 <screen>
-kea-shell [--host hostname] [--port number] [--timeout seconds] [command]
+kea-shell [--host hostname] [--port number] [--timeout seconds] [--service service-name] [command]
 </screen>
     where:
     </para>
@@ -53,6 +53,15 @@ kea-shell [--host hostname] [--port number] [--timeout seconds] [command]
 
       <listitem>
         <simpara>
+          <command>--service <replaceable>serive-name</replaceable></command> specifies the
+          target of a command. If not given, CA will be used as target.  May be used more
+          than once to specify multiple targets.
+        </simpara>
+      </listitem>
+
+
+      <listitem>
+        <simpara>
           <command>command</command> specifies the command to be sent. If not specified,
           <command>list-commands</command> command is used.
         </simpara>
@@ -89,18 +98,19 @@ kea-shell [--host hostname] [--port number] [--timeout seconds] [command]
 
     <para>The following shows a simple example of usage:
 <screen>
-$ <userinput>kea-shell --host 192.0.2.1 --port 8001 list-commands</userinput>
+$ <userinput>kea-shell --host 192.0.2.1 --port 8001 --service dhcp4 list-commands</userinput>
 ^D
 </screen>
     After the command line is entered, the program waits for command parameters to be entered.
     Since <command>list-commands</command> does not take any
     arguments, CTRL-D (represented in the above example by "^D") is pressed to indicate end
     of file (and so terminate the parameter input). The Shell will then contact
-    the CA and print out the list of available commands returned.
+    the CA and print out the list of available commands returned for the service named <command>dhcp4</command>.
     </para>
 
     <para>It is envisaged that Kea Shell will be most frequently used in scripts. The next example
-    shows a simple scripted execution. It sends the command "config-write" to the CA, along
+    shows a simple scripted execution. It sends the command "config-write" to the CA
+    (<command> --service </command> parameter hasn't been used), along
     with the parameters specified in param.json. The result will be stored in result.json.
 <screen>
 $ cat param.json

+ 4 - 0
src/bin/shell/kea-shell.in

@@ -63,6 +63,9 @@ def shell_body():
     parser.add_argument('--timeout', type=int, default='10',
                         help='Timeout (in seconds) when attempting to '
                         'connect to CA (default: 10)')
+    parser.add_argument('--service', nargs="?", action="append",
+                        help='target spcified service. If not specidied,'
+                        'control agent will receive command.')
     parser.add_argument('command', type=str, nargs="?",
                         default='list-commands',
                         help='command to be executed. If not specified, '
@@ -78,6 +81,7 @@ def shell_body():
     # used by the connection.
     params = CARequest()
     params.command = cmd_args.command
+    params.service = cmd_args.service
     params.http_host = cmd_args.host
     params.http_port = cmd_args.port
     params.timeout = cmd_args.timeout

+ 11 - 1
src/bin/shell/kea-shell.xml

@@ -40,7 +40,7 @@
 
   <docinfo>
     <copyright>
-      <year>2016</year>
+      <year>2017</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -53,6 +53,7 @@
       <arg><option>--host</option></arg>
       <arg><option>--port</option></arg>
       <arg><option>--timeout</option></arg>
+      <arg><option>--service</option></arg>
       <arg><option>command</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -117,6 +118,15 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>--service</option></term>
+        <listitem><para>
+          Specifies the service that is the target of a command. If not
+          specified, Control Agent will be targeted. May be used more than
+          once to specify multiple targets.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>command</option></term>
         <listitem><para>
           Specifies the command to be sent to CA. If not

+ 6 - 0
src/bin/shell/kea_conn.py

@@ -16,6 +16,7 @@ class CARequest:
      - http_host - hostname of the CA
      - http-port - TCP port of the CA
      - command - specifies the command to send (e.g. list-commands)
+     - service - specifies service that is target for the command (e.g. dhcp4)
      - timeout - timeout (in ms)
      - args - extra arguments my be added here
      - headers - extra HTTP headers may be added here
@@ -25,6 +26,7 @@ class CARequest:
     http_host = ''
     http_port = 0
     command = ''
+    service = ''
     timeout = 0
     args = ''
     headers = {}
@@ -39,6 +41,10 @@ class CARequest:
         this stores the output in self.content
         """
         self.content = '{ "command": "' + self.command + '"'
+        if self.service is not None:
+            self.service = [x for x in self.service if x]
+            if len(self.service) > 0:
+                self.content += ', "service": ["' +  '","'.join(self.service) + '"]'
         if len(self.args) > 1:
             self.content += ', "arguments": { ' + self.args + ' }'
         self.content += ' }'

+ 33 - 0
src/bin/shell/tests/shell_unittest.py.in

@@ -26,6 +26,39 @@ class CARequestUnitTest(unittest.TestCase):
         """
         pass
 
+    def test_body_with_service(self):
+        """
+        This test verifies if the CARequest object generates the request
+        content properly when there is one target service.
+        """
+        request = CARequest()
+        request.command = "foo"
+        request.service= ["service1"]
+        request.generate_body()
+        self.assertEqual(request.content, '{ "command": "foo", "service": ["service1"] }')
+
+    def test_body_with_multiple_service(self):
+        """
+        This test verifies if the CARequest object generates the request
+        content properly when there are two target service.
+        """
+        request = CARequest()
+        request.command = "foo"
+        request.service= ["service1","service2/2"]
+        request.generate_body()
+        self.assertEqual(request.content, '{ "command": "foo", "service": ["service1","service2/2"] }')
+
+    def test_body_with_malformed_service(self):
+        """
+        This test verifies if the CARequest object generates the request
+        content properly when there are two target service, one is empty
+        """
+        request = CARequest()
+        request.command = "foo"
+        request.service= ["service1",""]
+        request.generate_body()
+        self.assertEqual(request.content, '{ "command": "foo", "service": ["service1"] }')
+
     def test_body_without_args(self):
         """
         This test verifies if the CARequest object generates the request

+ 24 - 8
src/lib/http/connection.cc

@@ -34,10 +34,6 @@ HttpConnection:: HttpConnection(asiolink::IOService& io_service,
     : request_timer_(io_service),
       request_timeout_(request_timeout),
       socket_(io_service),
-      socket_callback_(boost::bind(&HttpConnection::socketReadCallback, this,
-                                   _1, _2)),
-      socket_write_callback_(boost::bind(&HttpConnection::socketWriteCallback,
-                                         this, _1, _2)),
       acceptor_(acceptor),
       connection_pool_(connection_pool),
       response_creator_(response_creator),
@@ -54,6 +50,7 @@ HttpConnection::~HttpConnection() {
 
 void
 HttpConnection::close() {
+    request_timer_.cancel();
     socket_.close();
 }
 
@@ -71,8 +68,12 @@ HttpConnection::stopThisConnection() {
 
 void
 HttpConnection::asyncAccept() {
+    // Create instance of the callback. It is safe to pass the local instance
+    // of the callback, because the underlying boost functions make copies
+    // as needed.
     HttpAcceptorCallback cb = boost::bind(&HttpConnection::acceptorCallback,
-                                          this, _1);
+                                          shared_from_this(),
+                                          boost::asio::placeholders::error);
     try {
         acceptor_.asyncAccept(socket_, cb);
 
@@ -86,8 +87,15 @@ void
 HttpConnection::doRead() {
     try {
         TCPEndpoint endpoint;
+        // Create instance of the callback. It is safe to pass the local instance
+        // of the callback, because the underlying boost functions make copies
+        // as needed.
+        SocketCallback cb(boost::bind(&HttpConnection::socketReadCallback,
+                                      shared_from_this(),
+                                      boost::asio::placeholders::error,
+                                      boost::asio::placeholders::bytes_transferred));
         socket_.asyncReceive(static_cast<void*>(buf_.data()), buf_.size(),
-                             0, &endpoint, socket_callback_);
+                             0, &endpoint, cb);
 
     } catch (const std::exception& ex) {
         stopThisConnection();
@@ -98,9 +106,16 @@ void
 HttpConnection::doWrite() {
     try {
         if (!output_buf_.empty()) {
+            // Create instance of the callback. It is safe to pass the local instance
+            // of the callback, because the underlying boost functions make copies
+            // as needed.
+            SocketCallback cb(boost::bind(&HttpConnection::socketWriteCallback,
+                                          shared_from_this(),
+                                          boost::asio::placeholders::error,
+                                          boost::asio::placeholders::bytes_transferred));
             socket_.asyncSend(output_buf_.data(),
                               output_buf_.length(),
-                              socket_write_callback_);
+                              cb);
         } else {
             stopThisConnection();
         }
@@ -133,7 +148,8 @@ HttpConnection::acceptorCallback(const boost::system::error_code& ec) {
                   HTTP_REQUEST_RECEIVE_START)
             .arg(getRemoteEndpointAddressAsText())
             .arg(static_cast<unsigned>(request_timeout_/1000));
-        request_timer_.setup(boost::bind(&HttpConnection::requestTimeoutCallback, this),
+        request_timer_.setup(boost::bind(&HttpConnection::requestTimeoutCallback,
+                                         shared_from_this()),
                              request_timeout_, IntervalTimer::ONE_SHOT);
         doRead();
     }

+ 0 - 6
src/lib/http/connection.h

@@ -187,12 +187,6 @@ private:
     /// @brief Socket used by this connection.
     asiolink::TCPSocket<SocketCallback> socket_;
 
-    /// @brief Callback invoked when data received over the socket.
-    SocketCallback socket_callback_;
-
-    /// @brief Callback invoked when data sent over the socket.
-    SocketCallback socket_write_callback_;
-
     /// @brief Reference to the TCP acceptor used to accept new connections.
     HttpAcceptor& acceptor_;
 

+ 28 - 10
src/lib/http/tests/listener_unittests.cc

@@ -168,10 +168,21 @@ public:
                            [this, request](const boost::system::error_code& ec,
                                            std::size_t bytes_transferred) mutable {
             if (ec) {
-                ADD_FAILURE() << "error occurred while connecting: "
-                              << ec.message();
-                io_service_.stop();
-                return;
+                if (ec.value() == boost::asio::error::operation_aborted) {
+                    return;
+
+                } else if ((ec.value() == boost::asio::error::try_again) ||
+                           (ec.value() == boost::asio::error::would_block)) {
+                    // If we should try again make sure there is no garbage in the
+                    // bytes_transferred.
+                    bytes_transferred = 0;
+
+                } else {
+                    ADD_FAILURE() << "error occurred while connecting: "
+                                  << ec.message();
+                    io_service_.stop();
+                    return;
+                }
             }
 
             // Remove the part of the request which has been sent.
@@ -199,14 +210,21 @@ public:
                                        std::size_t bytes_transferred) {
             if (ec) {
                 // IO service stopped so simply return.
-                if (ec == boost::asio::error::operation_aborted) {
+                if (ec.value() == boost::asio::error::operation_aborted) {
                     return;
-                }
 
-                // Error occurred, bail...
-                ADD_FAILURE() << "error occurred while receiving HTTP"
-                    " response from the server: " << ec.message();
-                io_service_.stop();
+                } else if ((ec.value() == boost::asio::error::try_again) ||
+                           (ec.value() == boost::asio::error::would_block)) {
+                    // If we should try again, make sure that there is no garbage
+                    // in the bytes_transferred.
+                    bytes_transferred = 0;
+
+                } else {
+                    // Error occurred, bail...
+                    ADD_FAILURE() << "error occurred while receiving HTTP"
+                        " response from the server: " << ec.message();
+                    io_service_.stop();
+                }
             }
 
             if (bytes_transferred > 0) {