shill: vpn: Implement VPNService::GetStorageIdentifier.

BUG=chromium-os:26988
TEST=unit tests

Change-Id: I7b1708b9208c4222240cfe2be64bca0e84037290
Reviewed-on: https://gerrit.chromium.org/gerrit/17827
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Ready: Darin Petkov <petkov@chromium.org>
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 8b4269e..efa4b86 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -889,6 +889,8 @@
   Error e;
   args.SetString(flimflam::kTypeProperty, flimflam::kTypeVPN);
   args.SetString(flimflam::kProviderTypeProperty, flimflam::kProviderOpenVpn);
+  args.SetString(flimflam::kProviderHostProperty, "10.8.0.1");
+  args.SetString(flimflam::kProviderNameProperty, "vpn-name");
   ServiceRefPtr service = manager()->GetService(args, &e);
   EXPECT_TRUE(e.IsSuccess());
   EXPECT_TRUE(service);
diff --git a/vpn_provider.cc b/vpn_provider.cc
index 2a9bd96..be3c466 100644
--- a/vpn_provider.cc
+++ b/vpn_provider.cc
@@ -41,6 +41,11 @@
     return NULL;
   }
 
+  string storage_id = VPNService::CreateStorageIdentifier(args, error);
+  if (storage_id.empty()) {
+    return NULL;
+  }
+
   const string &type = args.GetString(flimflam::kProviderTypeProperty);
   scoped_ptr<VPNDriver> driver;
   if (type == flimflam::kProviderOpenVpn) {
@@ -55,6 +60,7 @@
 
   VPNServiceRefPtr service = new VPNService(
       control_interface_, dispatcher_, metrics_, manager_, driver.release());
+  service->set_storage_id(storage_id);
   services_.push_back(service);
   manager_->RegisterService(service);
   return service;
diff --git a/vpn_provider_unittest.cc b/vpn_provider_unittest.cc
index 68eb4be..1f7c398 100644
--- a/vpn_provider_unittest.cc
+++ b/vpn_provider_unittest.cc
@@ -32,6 +32,9 @@
   virtual ~VPNProviderTest() {}
 
  protected:
+  static const char kHost[];
+  static const char kName[];
+
   NiceMockControl control_;
   MockMetrics metrics_;
   MockManager manager_;
@@ -39,7 +42,10 @@
   VPNProvider provider_;
 };
 
-TEST_F(VPNProviderTest, GetServiceVPNNoType) {
+const char VPNProviderTest::kHost[] = "10.8.0.1";
+const char VPNProviderTest::kName[] = "vpn-name";
+
+TEST_F(VPNProviderTest, GetServiceNoType) {
   KeyValueStore args;
   Error e;
   args.SetString(flimflam::kTypeProperty, flimflam::kTypeVPN);
@@ -48,26 +54,31 @@
   EXPECT_FALSE(service);
 }
 
-TEST_F(VPNProviderTest, GetServiceVPNUnsupportedType) {
+TEST_F(VPNProviderTest, GetServiceUnsupportedType) {
   KeyValueStore args;
   Error e;
   args.SetString(flimflam::kTypeProperty, flimflam::kTypeVPN);
   args.SetString(flimflam::kProviderTypeProperty, "unknown-vpn-type");
+  args.SetString(flimflam::kProviderHostProperty, kHost);
+  args.SetString(flimflam::kProviderNameProperty, kName);
   VPNServiceRefPtr service = provider_.GetService(args, &e);
   EXPECT_EQ(Error::kNotSupported, e.type());
   EXPECT_FALSE(service);
 }
 
-TEST_F(VPNProviderTest, GetServiceVPN) {
+TEST_F(VPNProviderTest, GetService) {
   KeyValueStore args;
   Error e;
   args.SetString(flimflam::kTypeProperty, flimflam::kTypeVPN);
   args.SetString(flimflam::kProviderTypeProperty, flimflam::kProviderOpenVpn);
+  args.SetString(flimflam::kProviderHostProperty, kHost);
+  args.SetString(flimflam::kProviderNameProperty, kName);
   EXPECT_CALL(manager_, device_info()).WillOnce(Return(&device_info_));
   EXPECT_CALL(manager_, RegisterService(_));
   VPNServiceRefPtr service = provider_.GetService(args, &e);
   EXPECT_TRUE(e.IsSuccess());
-  EXPECT_TRUE(service);
+  ASSERT_TRUE(service);
+  EXPECT_EQ("vpn_10_8_0_1_vpn_name", service->GetStorageIdentifier());
 }
 
 TEST_F(VPNProviderTest, OnDeviceInfoAvailable) {
diff --git a/vpn_service.cc b/vpn_service.cc
index 469feeb..3e3a7b9 100644
--- a/vpn_service.cc
+++ b/vpn_service.cc
@@ -4,11 +4,18 @@
 
 #include "shill/vpn_service.h"
 
-#include <base/logging.h>
+#include <algorithm>
 
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/key_value_store.h"
 #include "shill/technology.h"
 #include "shill/vpn_driver.h"
 
+using base::StringPrintf;
+using std::replace_if;
 using std::string;
 
 namespace shill {
@@ -34,8 +41,33 @@
 }
 
 string VPNService::GetStorageIdentifier() const {
-  NOTIMPLEMENTED();
-  return "";
+  return storage_id_;
+}
+
+// static
+string VPNService::CreateStorageIdentifier(const KeyValueStore &args,
+                                           Error *error) {
+  string host;
+  if (args.ContainsString(flimflam::kProviderHostProperty)) {
+    host = args.GetString(flimflam::kProviderHostProperty);
+  }
+  if (host.empty()) {
+    Error::PopulateAndLog(
+        error, Error::kInvalidProperty, "Missing VPN host.");
+    return "";
+  }
+  string name;
+  if (args.ContainsString(flimflam::kProviderNameProperty)) {
+    name = args.GetString(flimflam::kProviderNameProperty);
+  }
+  if (name.empty()) {
+    Error::PopulateAndLog(
+        error, Error::kNotSupported, "Missing VPN name.");
+    return "";
+  }
+  string id = StringPrintf("vpn_%s_%s", host.c_str(), name.c_str());
+  replace_if(id.begin(), id.end(), &Service::IllegalChar, '_');
+  return id;
 }
 
 string VPNService::GetDeviceRpcId(Error *error) {
diff --git a/vpn_service.h b/vpn_service.h
index 2a3ada1..a004cc7 100644
--- a/vpn_service.h
+++ b/vpn_service.h
@@ -12,6 +12,7 @@
 
 namespace shill {
 
+class KeyValueStore;
 class VPNDriver;
 
 class VPNService : public Service {
@@ -30,11 +31,16 @@
 
   VPNDriver *driver() const { return driver_.get(); }
 
+  static std::string CreateStorageIdentifier(const KeyValueStore &args,
+                                             Error *error);
+  void set_storage_id(const std::string &id) { storage_id_ = id; }
+
  private:
   FRIEND_TEST(VPNServiceTest, GetDeviceRpcId);
 
   virtual std::string GetDeviceRpcId(Error *error);
 
+  std::string storage_id_;
   scoped_ptr<VPNDriver> driver_;
 
   DISALLOW_COPY_AND_ASSIGN(VPNService);
diff --git a/vpn_service_unittest.cc b/vpn_service_unittest.cc
index cab404e..10a90b4 100644
--- a/vpn_service_unittest.cc
+++ b/vpn_service_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "shill/vpn_service.h"
 
+#include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 
 #include "shill/error.h"
@@ -20,8 +21,7 @@
  public:
   VPNServiceTest()
       : driver_(new MockVPNDriver()),
-        service_(new VPNService(&control_, NULL, &metrics_, NULL,
-                                driver_)) {}
+        service_(new VPNService(&control_, NULL, &metrics_, NULL, driver_)) {}
 
   virtual ~VPNServiceTest() {}
 
@@ -46,8 +46,36 @@
   EXPECT_TRUE(error.IsSuccess());
 }
 
+TEST_F(VPNServiceTest, CreateStorageIdentifierNoHost) {
+  KeyValueStore args;
+  Error error;
+  args.SetString(flimflam::kProviderNameProperty, "vpn-name");
+  EXPECT_EQ("", VPNService::CreateStorageIdentifier(args, &error));
+  EXPECT_EQ(Error::kInvalidProperty, error.type());
+}
+
+TEST_F(VPNServiceTest, CreateStorageIdentifierNoName) {
+  KeyValueStore args;
+  Error error;
+  args.SetString(flimflam::kProviderHostProperty, "10.8.0.1");
+  EXPECT_EQ("", VPNService::CreateStorageIdentifier(args, &error));
+  EXPECT_EQ(Error::kNotSupported, error.type());
+}
+
+TEST_F(VPNServiceTest, CreateStorageIdentifier) {
+  KeyValueStore args;
+  Error error;
+  args.SetString(flimflam::kProviderNameProperty, "vpn-name");
+  args.SetString(flimflam::kProviderHostProperty, "10.8.0.1");
+  EXPECT_EQ("vpn_10_8_0_1_vpn_name",
+            VPNService::CreateStorageIdentifier(args, &error));
+  EXPECT_TRUE(error.IsSuccess());
+}
+
 TEST_F(VPNServiceTest, GetStorageIdentifier) {
   EXPECT_EQ("", service_->GetStorageIdentifier());
+  service_->set_storage_id("foo");
+  EXPECT_EQ("foo", service_->GetStorageIdentifier());
 }
 
 TEST_F(VPNServiceTest, GetDeviceRpcId) {