Repository: sacloud/terraform-provider-sakuracloud
Pull request #648 (2020-01-22 04:41:01)

ローカルルータ対応
ローカルルータに対応する。

**注: v2のみの対応でv1は対応しない**

利用例:

```hcl
resource "sakuracloud_switch" "foobar" {
  name = "example"
}

resource "sakuracloud_local_router" "foobar" {
  name        = "{{ .arg0 }}-upd"
  description = "descriptio"
  tags        = ["tag1", "tag2"]

  # 接続するスイッチ(現在はクラウドにのみ対応。VPS/専用サーバは非対応)
  switch {
    code     = sakuracloud_switch.foobar.id
    category = "cloud"
    zone_id  = "is1a"
  }

  # NICの設定
  network_interface {
    vip          = "192.168.11.1"
    ip_addresses = ["192.168.11.11", "192.168.11.12"]
    netmask      = 24
    vrid         = 101
  }

  # スタティックルート
  static_route {
    prefix   = "10.0.0.0/24"
    next_hop = "192.168.11.2"
  }
  static_route {
    prefix   = "172.16.0.0/16"
    next_hop = "192.168.11.3"
  }

  # ピア設定
  peer {
    peer_id     = "000000000001" #ピアとなるローカルルータのID
    secret_key  = "xxx"
    description = "description"
  }
}
```


Patch:
diff --git a/.gitignore b/.gitignore
index b9492a17..ee2faf85 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@
 terraform.tfstate
 terraform.tfstate.backup
 releases-terraform
+website-current
+website-scaffold
diff --git a/examples/d/local_router.tf b/examples/d/local_router.tf
new file mode 100644
index 00000000..934394c4
--- /dev/null
+++ b/examples/d/local_router.tf
@@ -0,0 +1,5 @@
+data "sakuracloud_local_router" "foobar" {
+  filter {
+    names = ["foobar"]
+  }
+}
\ No newline at end of file
diff --git a/examples/r/database.tf b/examples/r/database.tf
index 42e7f268..5212784a 100644
--- a/examples/r/database.tf
+++ b/examples/r/database.tf
@@ -17,10 +17,10 @@ resource "sakuracloud_database" "foobar" {
   backup_time     = "00:00"
   backup_weekdays = ["mon", "tue"]
 
-  switch_id    = sakuracloud_switch.foobar.id
-  ip_address   = "192.168.11.11"
-  netmask      = 24
-  gateway      = "192.168.11.1"
+  switch_id  = sakuracloud_switch.foobar.id
+  ip_address = "192.168.11.11"
+  netmask    = 24
+  gateway    = "192.168.11.1"
 
   name        = "foobar"
   description = "description"
diff --git a/examples/r/database_read_replica.tf b/examples/r/database_read_replica.tf
index a25d30b8..19e95deb 100644
--- a/examples/r/database_read_replica.tf
+++ b/examples/r/database_read_replica.tf
@@ -1,9 +1,9 @@
 resource "sakuracloud_database_read_replica" "foobar" {
-  master_id    = data.sakuracloud_database.master.id
-  ip_address   = "192.168.11.111"
-  name         = "foobar"
-  description  = "description"
-  tags         = ["tag1", "tag2"]
+  master_id   = data.sakuracloud_database.master.id
+  ip_address  = "192.168.11.111"
+  name        = "foobar"
+  description = "description"
+  tags        = ["tag1", "tag2"]
 }
 
 data sakuracloud_database "master" {
diff --git a/examples/r/local_router.tf b/examples/r/local_router.tf
new file mode 100644
index 00000000..efbaad93
--- /dev/null
+++ b/examples/r/local_router.tf
@@ -0,0 +1,43 @@
+resource "sakuracloud_local_router" "foobar" {
+  name        = "example"
+  description = "descriptio"
+  tags        = ["tag1", "tag2"]
+
+  switch {
+    code     = sakuracloud_switch.foobar.id
+    category = "cloud"
+    zone_id  = "is1a"
+  }
+
+  network_interface {
+    vip          = "192.168.11.1"
+    ip_addresses = ["192.168.11.11", "192.168.11.12"]
+    netmask      = 24
+    vrid         = 101
+  }
+
+  static_route {
+    prefix   = "10.0.0.0/24"
+    next_hop = "192.168.11.2"
+  }
+  static_route {
+    prefix   = "172.16.0.0/16"
+    next_hop = "192.168.11.3"
+  }
+
+  peer {
+    peer_id     = data.sakuracloud_local_router.peer.id
+    secret_key  = data.sakuracloud_local_router.secret_keys[0]
+    description = "description"
+  }
+}
+
+resource "sakuracloud_switch" "foobar" {
+  name = "example"
+}
+
+data "sakuracloud_local_router" "peer" {
+  filter {
+    names = ["peer"]
+  }
+}
diff --git a/go.mod b/go.mod
index b7199cc8..d973ed0b 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/motain/gocheck v0.0.0-20131023154940-9beb271d26e6 // indirect
 	github.com/sacloud/ftps v0.0.0-20171205062625-42fc0f9886fe
 	github.com/sacloud/iso9660wrap v0.0.0-20171031075302-eda21f77f6a8
-	github.com/sacloud/libsacloud/v2 v2.0.0-rc3
+	github.com/sacloud/libsacloud/v2 v2.0.0-rc5
 	github.com/stretchr/testify v1.4.0
 	github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec // indirect
 	golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
diff --git a/go.sum b/go.sum
index bf0983e4..642be9b2 100644
--- a/go.sum
+++ b/go.sum
@@ -198,8 +198,8 @@ github.com/sacloud/ftps v0.0.0-20171205062625-42fc0f9886fe h1:JSZLn4B8X9V1ynSEht
 github.com/sacloud/ftps v0.0.0-20171205062625-42fc0f9886fe/go.mod h1:sdVeG85LaUt1f+0meZqSf6hSQYlwdgFLI8tJqLZ58VM=
 github.com/sacloud/iso9660wrap v0.0.0-20171031075302-eda21f77f6a8 h1:5piK7EELHKRxGqBjgVEHsdfsFwEzF/Kds/bMWLS6gCw=
 github.com/sacloud/iso9660wrap v0.0.0-20171031075302-eda21f77f6a8/go.mod h1:1jHHCa624cG5rODkWmourombJaNmkY9/SBHWhLJcg+w=
-github.com/sacloud/libsacloud/v2 v2.0.0-rc3 h1:5Xl3wMR2fK4aJZxDh8u50RxjKwmSIZL/RzZpvitGfV4=
-github.com/sacloud/libsacloud/v2 v2.0.0-rc3/go.mod h1:BG2crADa3HJrADiZK2zK1/UxsVUtWSy6q7nd6YqleD4=
+github.com/sacloud/libsacloud/v2 v2.0.0-rc5 h1:qpNDv3AqKNEAudQCM7Nh5IAYSV8L8ducO7egb6oSTgQ=
+github.com/sacloud/libsacloud/v2 v2.0.0-rc5/go.mod h1:BG2crADa3HJrADiZK2zK1/UxsVUtWSy6q7nd6YqleD4=
 github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
diff --git a/sakuracloud/data_source_sakuracloud_local_router.go b/sakuracloud/data_source_sakuracloud_local_router.go
new file mode 100644
index 00000000..7819c09f
--- /dev/null
+++ b/sakuracloud/data_source_sakuracloud_local_router.go
@@ -0,0 +1,160 @@
+// Copyright 2016-2020 terraform-provider-sakuracloud authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sakuracloud
+
+import (
+	"fmt"
+
+	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
+	"github.com/sacloud/libsacloud/v2/sacloud"
+)
+
+func dataSourceSakuraCloudLocalRouter() *schema.Resource {
+	resourceName := "LocalRouter"
+	return &schema.Resource{
+		Read: dataSourceSakuraCloudLocalRouterRead,
+
+		Schema: map[string]*schema.Schema{
+			filterAttrName: filterSchema(&filterSchemaOption{}),
+			"name":         schemaDataSourceName(resourceName),
+			"switch": {
+				Type:     schema.TypeList,
+				Computed: true,
+				Elem: &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"code": {
+							Type:        schema.TypeString,
+							Computed:    true,
+							Description: "The resource ID of the Switch",
+						},
+						"category": {
+							Type:        schema.TypeString,
+							Computed:    true,
+							Description: "The category name of connected services (e.g. `cloud`, `vps`)",
+						},
+						"zone_id": {
+							Type:        schema.TypeString,
+							Computed:    true,
+							Description: "The id of the Zone",
+						},
+					},
+				},
+			},
+			"network_interface": {
+				Type:     schema.TypeList,
+				Computed: true,
+				Elem: &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"vip": {
+							Type:        schema.TypeString,
+							Computed:    true,
+							Description: "The virtual IP address",
+						},
+						"ip_addresses": schemaDataSourceIPAddresses(resourceName),
+						"netmask":      schemaDataSourceNetMask(resourceName),
+						"vrid": {
+							Type:        schema.TypeInt,
+							Computed:    true,
+							Description: "The Virtual Router Identifier",
+						},
+					},
+				},
+			},
+			"peer": {
+				Type:     schema.TypeList,
+				Computed: true,
+				Elem: &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"peer_id": {
+							Type:        schema.TypeString,
+							Computed:    true,
+							Description: "The ID of the peer LocalRouter",
+						},
+						"secret_key": {
+							Type:        schema.TypeString,
+							Computed:    true,
+							Sensitive:   true,
+							Description: "The secret key of the peer LocalRouter",
+						},
+						"enabled": {
+							Type:        schema.TypeBool,
+							Computed:    true,
+							Description: "The flag to enable the LocalRouter",
+						},
+						"description": schemaDataSourceDescription(resourceName),
+					},
+				},
+			},
+			"static_route": {
+				Type:     schema.TypeList,
+				Computed: true,
+				Elem: &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"prefix": {
+							Type:        schema.TypeString,
+							Computed:    true,
+							Description: "The CIDR block of destination",
+						},
+						"next_hop": {
+							Type:        schema.TypeString,
+							Computed:    true,
+							Description: "The IP address of the next hop",
+						},
+					},
+				},
+			},
+			"icon_id":     schemaDataSourceIconID(resourceName),
+			"description": schemaDataSourceDescription(resourceName),
+			"tags":        schemaDataSourceTags(resourceName),
+			"secret_keys": {
+				Type: schema.TypeList,
+				Elem: &schema.Schema{
+					Type:      schema.TypeString,
+					Sensitive: true,
+				},
+				Computed:    true,
+				Description: "A list of secret key used for peering from other LocalRouters",
+			},
+		},
+	}
+}
+
+func dataSourceSakuraCloudLocalRouterRead(d *schema.ResourceData, meta interface{}) error {
+	client, _, err := sakuraCloudClient(d, meta)
+	if err != nil {
+		return err
+	}
+	ctx, cancel := operationContext(d, schema.TimeoutRead)
+	defer cancel()
+
+	searcher := sacloud.NewLocalRouterOp(client)
+
+	findCondition := &sacloud.FindCondition{}
+	if rawFilter, ok := d.GetOk(filterAttrName); ok {
+		findCondition.Filter = expandSearchFilter(rawFilter)
+	}
+
+	res, err := searcher.Find(ctx, findCondition)
+	if err != nil {
+		return fmt.Errorf("could not find SakuraCloud LocalRouter resource: %s", err)
+	}
+	if res == nil || res.Count == 0 || len(res.LocalRouters) == 0 {
+		return filterNoResultErr()
+	}
+
+	targets := res.LocalRouters
+	d.SetId(targets[0].ID.String())
+	return setLocalRouterResourceData(ctx, d, client, targets[0])
+}
diff --git a/sakuracloud/deletion_waiter.go b/sakuracloud/deletion_waiter.go
index a526eaa0..944e060a 100644
--- a/sakuracloud/deletion_waiter.go
+++ b/sakuracloud/deletion_waiter.go
@@ -65,6 +65,7 @@ func waitForDeletionByDiskID(ctx context.Context, client *APIClient, zone string
 
 func waitForDeletionBySwitchID(ctx context.Context, client *APIClient, zone string, switchID types.ID) error {
 	return waitForDeletion(ctx, client, zone, switchID, []deletionWaiterFindFunc{
+		switchHasHybridConnection,
 		findServerBySwitchID,
 		findLoadBalancerBySwitchID,
 		findVPCRouterBySwitchID,
@@ -163,6 +164,15 @@ func waitForDeletionByFunc(ctx context.Context, client *APIClient, zone string,
 	}
 }
 
+func switchHasHybridConnection(ctx context.Context, client *APIClient, zone string, id types.ID) (bool, error) {
+	swOp := sacloud.NewSwitchOp(client)
+	sw, err := swOp.Read(ctx, zone, id)
+	if err != nil {
+		return false, fmt.Errorf("reading switch is failed: %s", err)
+	}
+	return !sw.HybridConnectionID.IsEmpty(), nil
+}
+
 func findServerBySwitchID(ctx context.Context, client *APIClient, zone string, id types.ID) (bool, error) {
 	swOp := sacloud.NewSwitchOp(client)
 
diff --git a/sakuracloud/provider.go b/sakuracloud/provider.go
index f5e702ee..f3445955 100644
--- a/sakuracloud/provider.go
+++ b/sakuracloud/provider.go
@@ -146,6 +146,7 @@ func Provider() terraform.ResourceProvider {
 			"sakuracloud_icon":               dataSourceSakuraCloudIcon(),
 			"sakuracloud_internet":           dataSourceSakuraCloudInternet(),
 			"sakuracloud_load_balancer":      dataSourceSakuraCloudLoadBalancer(),
+			"sakuracloud_local_router":       dataSourceSakuraCloudLocalRouter(),
 			"sakuracloud_note":               dataSourceSakuraCloudNote(),
 			"sakuracloud_nfs":                dataSourceSakuraCloudNFS(),
 			"sakuracloud_packet_filter":      dataSourceSakuraCloudPacketFilter(),
@@ -177,6 +178,7 @@ func Provider() terraform.ResourceProvider {
 			"sakuracloud_internet":              resourceSakuraCloudInternet(),
 			"sakuracloud_ipv4_ptr":              resourceSakuraCloudIPv4Ptr(),
 			"sakuracloud_load_balancer":         resourceSakuraCloudLoadBalancer(),
+			"sakuracloud_local_router":          resourceSakuraCloudLocalRouter(),
 			"sakuracloud_mobile_gateway":        resourceSakuraCloudMobileGateway(),
 			"sakuracloud_note":                  resourceSakuraCloudNote(),
 			"sakuracloud_nfs":                   resourceSakuraCloudNFS(),
diff --git a/sakuracloud/resource_sakuracloud_local_router.go b/sakuracloud/resource_sakuracloud_local_router.go
new file mode 100644
index 00000000..5d6a4beb
--- /dev/null
+++ b/sakuracloud/resource_sakuracloud_local_router.go
@@ -0,0 +1,296 @@
+// Copyright 2016-2020 terraform-provider-sakuracloud authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sakuracloud
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
+	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
+	"github.com/sacloud/libsacloud/v2/sacloud"
+)
+
+func resourceSakuraCloudLocalRouter() *schema.Resource {
+	resourceName := "LocalRouter"
+
+	return &schema.Resource{
+		Create: resourceSakuraCloudLocalRouterCreate,
+		Read:   resourceSakuraCloudLocalRouterRead,
+		Update: resourceSakuraCloudLocalRouterUpdate,
+		Delete: resourceSakuraCloudLocalRouterDelete,
+		Importer: &schema.ResourceImporter{
+			State: schema.ImportStatePassthrough,
+		},
+
+		Timeouts: &schema.ResourceTimeout{
+			Create: schema.DefaultTimeout(20 * time.Minute),
+			Update: schema.DefaultTimeout(20 * time.Minute),
+			Delete: schema.DefaultTimeout(20 * time.Minute),
+		},
+
+		Schema: map[string]*schema.Schema{
+			"name": schemaResourceName(resourceName),
+			"switch": {
+				Type:     schema.TypeList,
+				MaxItems: 1,
+				MinItems: 1,
+				Required: true,
+				Elem: &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"code": {
+							Type:        schema.TypeString,
+							Required:    true,
+							Description: "The resource ID of the Switch",
+						},
+						"category": {
+							Type:        schema.TypeString,
+							Optional:    true,
+							Default:     "cloud",
+							Description: "The category name of connected services (e.g. `cloud`, `vps`)",
+						},
+						"zone_id": {
+							Type:        schema.TypeString,
+							Required:    true,
+							Description: "The id of the Zone",
+						},
+					},
+				},
+			},
+			"network_interface": {
+				Type:     schema.TypeList,
+				Required: true,
+				MinItems: 1,
+				MaxItems: 1,
+				Elem: &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"vip": {
+							Type:         schema.TypeString,
+							Required:     true,
+							ValidateFunc: validation.SingleIP(),
+							Description:  "The virtual IP address",
+						},
+						"ip_addresses": {
+							Type:        schema.TypeList,
+							Required:    true,
+							MinItems:    2,
+							MaxItems:    2,
+							Elem:        &schema.Schema{Type: schema.TypeString},
+							Description: descf("A list of IP address to assign to the %s. ", resourceName),
+						},
+						"netmask": {
+							Type:         schema.TypeInt,
+							Required:     true,
+							ValidateFunc: validation.IntBetween(8, 29),
+							Description: descf(
+								"The bit length of the subnet assigned to the %s. %s", resourceName,
+								descRange(8, 29),
+							),
+						},
+						"vrid": {
+							Type:        schema.TypeInt,
+							Required:    true,
+							Description: "The Virtual Router Identifier",
+						},
+					},
+				},
+			},
+			"peer": {
+				Type:     schema.TypeList,
+				Optional: true,
+				Elem: &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"peer_id": {
+							Type:         schema.TypeString,
+							Required:     true,
+							ValidateFunc: validateSakuracloudIDType,
+							Description:  "The ID of the peer LocalRouter",
+						},
+						"secret_key": {
+							Type:        schema.TypeString,
+							Required:    true,
+							Sensitive:   true,
+							Description: "The secret key of the peer LocalRouter",
+						},
+						"enabled": {
+							Type:        schema.TypeBool,
+							Optional:    true,
+							Default:     true,
+							Description: "The flag to enable the LocalRouter",
+						},
+						"description": schemaResourceDescription(resourceName),
+					},
+				},
+			},
+			"static_route": {
+				Type:     schema.TypeList,
+				Optional: true,
+				Elem: &schema.Resource{
+					Schema: map[string]*schema.Schema{
+						"prefix": {
+							Type:        schema.TypeString,
+							Required:    true,
+							Description: "The CIDR block of destination",
+						},
+						"next_hop": {
+							Type:         schema.TypeString,
+							Required:     true,
+							ValidateFunc: validation.SingleIP(),
+							Description:  "The IP address of the next hop",
+						},
+					},
+				},
+			},
+			"icon_id":     schemaResourceIconID(resourceName),
+			"description": schemaResourceDescription(resourceName),
+			"tags":        schemaResourceTags(resourceName),
+			"secret_keys": {
+				Type:        schema.TypeList,
+				Elem:        &schema.Schema{Type: schema.TypeString},
+				Computed:    true,
+				Description: "A list of secret key used for peering from other LocalRouters",
+			},
+		},
+	}
+}
+
+func resourceSakuraCloudLocalRouterCreate(d *schema.ResourceData, meta interface{}) error {
+	client, _, err := sakuraCloudClient(d, meta)
+	if err != nil {
+		return err
+	}
+	ctx, cancel := operationContext(d, schema.TimeoutCreate)
+	defer cancel()
+
+	builder := expandLocalRouterBuilder(d, client)
+	if err := builder.Validate(ctx); err != nil {
+		return fmt.Errorf("validating parameter for SakuraCloud LocalRouter is failed: %s", err)
+	}
+
+	localRouter, err := builder.Build(ctx)
+	if err != nil {
+		return fmt.Errorf("creating SakuraCloud LocalRouter is failed: %s", err)
+	}
+	d.SetId(localRouter.ID.String())
+	return resourceSakuraCloudLocalRouterRead(d, meta)
+}
+
+func resourceSakuraCloudLocalRouterRead(d *schema.ResourceData, meta interface{}) error {
+	client, _, err := sakuraCloudClient(d, meta)
+	if err != nil {
+		return err
+	}
+	ctx, cancel := operationContext(d, schema.TimeoutRead)
+	defer cancel()
+
+	lrOp := sacloud.NewLocalRouterOp(client)
+	localRouter, err := lrOp.Read(ctx, sakuraCloudID(d.Id()))
+	if err != nil {
+		if sacloud.IsNotFoundError(err) {
+			d.SetId("")
+			return nil
+		}
+		return fmt.Errorf("could not read SakuraCloud LocalRouter[%s]: %s", d.Id(), err)
+	}
+
+	return setLocalRouterResourceData(ctx, d, client, localRouter)
+}
+
+func resourceSakuraCloudLocalRouterUpdate(d *schema.ResourceData, meta interface{}) error {
+	client, _, err := sakuraCloudClient(d, meta)
+	if err != nil {
+		return err
+	}
+	ctx, cancel := operationContext(d, schema.TimeoutUpdate)
+	defer cancel()
+
+	lrOp := sacloud.NewLocalRouterOp(client)
+
+	sakuraMutexKV.Lock(d.Id())
+	defer sakuraMutexKV.Unlock(d.Id())
+
+	localRouter, err := lrOp.Read(ctx, sakuraCloudID(d.Id()))
+	if err != nil {
+		return fmt.Errorf("could not read SakuraCloud LocalRouter[%s]: %s", d.Id(), err)
+	}
+
+	builder := expandLocalRouterBuilder(d, client)
+	if err := builder.Validate(ctx); err != nil {
+		return fmt.Errorf("validating parameter for SakuraCloud LocalRouter is failed: %s", err)
+	}
+
+	localRouter, err = builder.Update(ctx, localRouter.ID)
+	if err != nil {
+		return fmt.Errorf("updating SakuraCloud LocalRouter[%s] is failed: %s", localRouter.ID, err)
+	}
+	return resourceSakuraCloudLocalRouterRead(d, meta)
+}
+
+func resourceSakuraCloudLocalRouterDelete(d *schema.ResourceData, meta interface{}) error {
+	client, _, err := sakuraCloudClient(d, meta)
+	if err != nil {
+		return err
+	}
+	ctx, cancel := operationContext(d, schema.TimeoutDelete)
+	defer cancel()
+
+	lrOp := sacloud.NewLocalRouterOp(client)
+
+	sakuraMutexKV.Lock(d.Id())
+	defer sakuraMutexKV.Unlock(d.Id())
+
+	localRouter, err := lrOp.Read(ctx, sakuraCloudID(d.Id()))
+	if err != nil {
+		if sacloud.IsNotFoundError(err) {
+			d.SetId("")
+			return nil
+		}
+		return fmt.Errorf("could not read SakuraCloud LocalRouter[%s]: %s", d.Id(), err)
+	}
+
+	if err := lrOp.Delete(ctx, localRouter.ID); err != nil {
+		return fmt.Errorf("deleting SakuraCloud LocalRouter[%s] is failed: %s", localRouter.ID, err)
+	}
+	return nil
+}
+
+func setLocalRouterResourceData(ctx context.Context, d *schema.ResourceData, client *APIClient, data *sacloud.LocalRouter) error {
+	if data.Availability.IsFailed() {
+		d.SetId("")
+		return fmt.Errorf("got unexpected state: LocalRouter[%d].Availability is failed", data.ID)
+	}
+
+	d.Set("name", data.Name)               // nolint
+	d.Set("icon_id", data.IconID.String()) // nolint
+	d.Set("description", data.Description) // nolint
+	d.Set("zone", getZone(d, client))      // nolint
+	if err := d.Set("secret_keys", data.SecretKeys); err != nil {
+		return err
+	}
+	if err := d.Set("switch", flattenLocalRouterSwitch(data)); err != nil {
+		return err
+	}
+	if err := d.Set("network_interface", flattenLocalRouterNetworkInterface(data)); err != nil {
+		return err
+	}
+	if err := d.Set("peer", flattenLocalRouterPeers(data)); err != nil {
+		return err
+	}
+	if err := d.Set("static_route", flattenLocalRouterStaticRoutes(data)); err != nil {
+		return err
+	}
+	return d.Set("tags", flattenTags(data.Tags))
+}
diff --git a/sakuracloud/structure_local_router.go b/sakuracloud/structure_local_router.go
new file mode 100644
index 00000000..9c8f0252
--- /dev/null
+++ b/sakuracloud/structure_local_router.go
@@ -0,0 +1,149 @@
+// Copyright 2016-2020 terraform-provider-sakuracloud authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sakuracloud
+
+import (
+	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
+	"github.com/sacloud/libsacloud/v2/sacloud"
+	"github.com/sacloud/libsacloud/v2/utils/builder/localrouter"
+)
+
+func expandLocalRouterBuilder(d *schema.ResourceData, client *APIClient) *localrouter.Builder {
+	return &localrouter.Builder{
+		Name:         stringOrDefault(d, "name"),
+		Description:  stringOrDefault(d, "description"),
+		Tags:         expandTags(d),
+		IconID:       expandSakuraCloudID(d, "icon_id"),
+		Switch:       expandLocalRouterSwitch(d),
+		Interface:    expandLocalRouterNetworkInterface(d),
+		Peers:        expandLocalRouterPeers(d),
+		StaticRoutes: expandLocalStaticRoutes(d),
+		Client:       localrouter.NewAPIClient(client),
+	}
+}
+
+func expandLocalRouterSwitch(d resourceValueGettable) *sacloud.LocalRouterSwitch {
+	if values, ok := getListFromResource(d, "switch"); ok && len(values) > 0 {
+		d = mapToResourceData(values[0].(map[string]interface{}))
+		return &sacloud.LocalRouterSwitch{
+			Code:     stringOrDefault(d, "code"),
+			Category: stringOrDefault(d, "category"),
+			ZoneID:   stringOrDefault(d, "zone_id"),
+		}
+	}
+	return nil
+}
+
+func flattenLocalRouterSwitch(data *sacloud.LocalRouter) []interface{} {
+	if data.Switch != nil {
+		return []interface{}{
+			map[string]interface{}{
+				"code":     data.Switch.Code,
+				"category": data.Switch.Category,
+				"zone_id":  data.Switch.ZoneID,
+			},
+		}
+	}
+	return nil
+}
+
+func expandLocalRouterNetworkInterface(d resourceValueGettable) *sacloud.LocalRouterInterface {
+	if values, ok := getListFromResource(d, "network_interface"); ok && len(values) > 0 {
+		d = mapToResourceData(values[0].(map[string]interface{}))
+		return &sacloud.LocalRouterInterface{
+			VirtualIPAddress: stringOrDefault(d, "vip"),
+			IPAddress:        expandStringList(d.Get("ip_addresses").([]interface{})),
+			NetworkMaskLen:   intOrDefault(d, "netmask"),
+			VRID:             intOrDefault(d, "vrid"),
+		}
+	}
+	return nil
+}
+
+func flattenLocalRouterNetworkInterface(data *sacloud.LocalRouter) []interface{} {
+	if data.Interface != nil {
+		return []interface{}{
+			map[string]interface{}{
+				"vip":          data.Interface.VirtualIPAddress,
+				"ip_addresses": data.Interface.IPAddress,
+				"netmask":      data.Interface.NetworkMaskLen,
+				"vrid":         data.Interface.VRID,
+			},
+		}
+	}
+	return nil
+}
+
+func expandLocalRouterPeers(d resourceValueGettable) []*sacloud.LocalRouterPeer {
+	var results []*sacloud.LocalRouterPeer
+	if values, ok := getListFromResource(d, "peer"); ok && len(values) > 0 {
+		for _, raw := range values {
+			d = mapToResourceData(raw.(map[string]interface{}))
+			results = append(results, &sacloud.LocalRouterPeer{
+				ID:          expandSakuraCloudID(d, "peer_id"),
+				SecretKey:   stringOrDefault(d, "secret_key"),
+				Enabled:     boolOrDefault(d, "enabled"),
+				Description: stringOrDefault(d, "description"),
+			})
+		}
+	}
+	return results
+}
+
+func flattenLocalRouterPeers(data *sacloud.LocalRouter) []interface{} {
+	var results []interface{}
+	for _, peer := range data.Peers {
+		results = append(results, flattenLocalRouterPeer(peer))
+	}
+	return results
+}
+
+func flattenLocalRouterPeer(data *sacloud.LocalRouterPeer) interface{} {
+	return map[string]interface{}{
+		"peer_id":     data.ID.String(),
+		"secret_key":  data.SecretKey,
+		"enabled":     data.Enabled,
+		"description": data.Description,
+	}
+}
+
+func expandLocalStaticRoutes(d resourceValueGettable) []*sacloud.LocalRouterStaticRoute {
+	var results []*sacloud.LocalRouterStaticRoute
+	if values, ok := getListFromResource(d, "static_route"); ok && len(values) > 0 {
+		for _, raw := range values {
+			d = mapToResourceData(raw.(map[string]interface{}))
+			results = append(results, &sacloud.LocalRouterStaticRoute{
+				Prefix:  stringOrDefault(d, "prefix"),
+				NextHop: stringOrDefault(d, "next_hop"),
+			})
+		}
+	}
+	return results
+}
+
+func flattenLocalRouterStaticRoutes(data *sacloud.LocalRouter) []interface{} {
+	var results []interface{}
+	for _, route := range data.StaticRoutes {
+		results = append(results, flattenLocalRouterStaticRoute(route))
+	}
+	return results
+}
+
+func flattenLocalRouterStaticRoute(data *sacloud.LocalRouterStaticRoute) interface{} {
+	return map[string]interface{}{
+		"prefix":   data.Prefix,
+		"next_hop": data.NextHop,
+	}
+}
diff --git a/tools/tfdocgen/cmd/gen-sakuracloud-docs/main.go b/tools/tfdocgen/cmd/gen-sakuracloud-docs/main.go
index 89960fe6..cc6ecd99 100644
--- a/tools/tfdocgen/cmd/gen-sakuracloud-docs/main.go
+++ b/tools/tfdocgen/cmd/gen-sakuracloud-docs/main.go
@@ -160,6 +160,10 @@ var definitions = map[string]definition{
 		displayName: "Load Balancer",
 		category:    CategoryAppliance,
 	},
+	"sakuracloud_local_router": {
+		displayName: "Local Router",
+		category:    CategoryNetworking,
+	},
 	"sakuracloud_mobile_gateway": {
 		displayName: "Mobile Gateway",
 		category:    CategorySecureMobile,
diff --git a/vendor/github.com/sacloud/libsacloud/v2/pkg/mapconv/mapconv.go b/vendor/github.com/sacloud/libsacloud/v2/pkg/mapconv/mapconv.go
index fdacbcc9..1cdf40da 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/pkg/mapconv/mapconv.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/pkg/mapconv/mapconv.go
@@ -131,7 +131,7 @@ func ConvertFrom(source interface{}, dest interface{}) error {
 			if err != nil {
 				return err
 			}
-			if value == nil {
+			if value == nil || reflect.ValueOf(value).IsZero() {
 				continue
 			}
 
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/json_file_store.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/json_file_store.go
index 5a57a921..95ae83e6 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/json_file_store.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/json_file_store.go
@@ -297,46 +297,48 @@ func (s *JSONFileStore) Delete(resourceKey, zone string, id types.ID) {
 }
 
 var jsonResourceTypeMap = map[string]func() interface{}{
-	ResourceArchive:         func() interface{} { return &sacloud.Archive{} },
-	ResourceAuthStatus:      func() interface{} { return &sacloud.AuthStatus{} },
-	ResourceAutoBackup:      func() interface{} { return &sacloud.AutoBackup{} },
-	ResourceBill:            func() interface{} { return &sacloud.Bill{} },
-	ResourceBridge:          func() interface{} { return &sacloud.Bridge{} },
-	ResourceCDROM:           func() interface{} { return &sacloud.CDROM{} },
-	ResourceCoupon:          func() interface{} { return &sacloud.Coupon{} },
-	ResourceDatabase:        func() interface{} { return &sacloud.Database{} },
-	ResourceDisk:            func() interface{} { return &sacloud.Disk{} },
-	ResourceDiskPlan:        func() interface{} { return &sacloud.DiskPlan{} },
-	ResourceDNS:             func() interface{} { return &sacloud.DNS{} },
-	ResourceGSLB:            func() interface{} { return &sacloud.GSLB{} },
-	ResourceIcon:            func() interface{} { return &sacloud.Icon{} },
-	ResourceInterface:       func() interface{} { return &sacloud.Interface{} },
-	ResourceInternet:        func() interface{} { return &sacloud.Internet{} },
-	ResourceInternetPlan:    func() interface{} { return &sacloud.InternetPlan{} },
-	ResourceIPAddress:       func() interface{} { return &sacloud.IPAddress{} },
-	ResourceIPv6Net:         func() interface{} { return &sacloud.IPv6Net{} },
-	ResourceIPv6Addr:        func() interface{} { return &sacloud.IPv6Addr{} },
-	ResourceLicense:         func() interface{} { return &sacloud.License{} },
-	ResourceLicenseInfo:     func() interface{} { return &sacloud.LicenseInfo{} },
-	ResourceLoadBalancer:    func() interface{} { return &sacloud.LoadBalancer{} },
-	ResourceMobileGateway:   func() interface{} { return &sacloud.MobileGateway{} },
-	ResourceNFS:             func() interface{} { return &sacloud.NFS{} },
-	ResourceNote:            func() interface{} { return &sacloud.Note{} },
-	ResourcePacketFilter:    func() interface{} { return &sacloud.PacketFilter{} },
-	ResourcePrivateHost:     func() interface{} { return &sacloud.PrivateHost{} },
-	ResourcePrivateHostPlan: func() interface{} { return &sacloud.PrivateHostPlan{} },
-	ResourceProxyLB:         func() interface{} { return &sacloud.ProxyLB{} },
-	ResourceRegion:          func() interface{} { return &sacloud.Region{} },
-	ResourceServer:          func() interface{} { return &sacloud.Server{} },
-	ResourceServerPlan:      func() interface{} { return &sacloud.ServerPlan{} },
-	ResourceServiceClass:    func() interface{} { return &sacloud.ServiceClass{} },
-	ResourceSIM:             func() interface{} { return &sacloud.SIM{} },
-	ResourceSimpleMonitor:   func() interface{} { return &sacloud.SimpleMonitor{} },
-	ResourceSSHKey:          func() interface{} { return &sacloud.SSHKey{} },
-	ResourceSwitch:          func() interface{} { return &sacloud.Switch{} },
-	ResourceVPCRouter:       func() interface{} { return &sacloud.VPCRouter{} },
-	ResourceWebAccel:        func() interface{} { return &sacloud.WebAccel{} },
-	ResourceZone:            func() interface{} { return &sacloud.Zone{} },
+	ResourceArchive:           func() interface{} { return &sacloud.Archive{} },
+	ResourceAuthStatus:        func() interface{} { return &sacloud.AuthStatus{} },
+	ResourceAutoBackup:        func() interface{} { return &sacloud.AutoBackup{} },
+	ResourceBill:              func() interface{} { return &sacloud.Bill{} },
+	ResourceBridge:            func() interface{} { return &sacloud.Bridge{} },
+	ResourceCDROM:             func() interface{} { return &sacloud.CDROM{} },
+	ResourceContainerRegistry: func() interface{} { return &sacloud.ContainerRegistry{} },
+	ResourceCoupon:            func() interface{} { return &sacloud.Coupon{} },
+	ResourceDatabase:          func() interface{} { return &sacloud.Database{} },
+	ResourceDisk:              func() interface{} { return &sacloud.Disk{} },
+	ResourceDiskPlan:          func() interface{} { return &sacloud.DiskPlan{} },
+	ResourceDNS:               func() interface{} { return &sacloud.DNS{} },
+	ResourceGSLB:              func() interface{} { return &sacloud.GSLB{} },
+	ResourceIcon:              func() interface{} { return &sacloud.Icon{} },
+	ResourceInterface:         func() interface{} { return &sacloud.Interface{} },
+	ResourceInternet:          func() interface{} { return &sacloud.Internet{} },
+	ResourceInternetPlan:      func() interface{} { return &sacloud.InternetPlan{} },
+	ResourceIPAddress:         func() interface{} { return &sacloud.IPAddress{} },
+	ResourceIPv6Net:           func() interface{} { return &sacloud.IPv6Net{} },
+	ResourceIPv6Addr:          func() interface{} { return &sacloud.IPv6Addr{} },
+	ResourceLicense:           func() interface{} { return &sacloud.License{} },
+	ResourceLicenseInfo:       func() interface{} { return &sacloud.LicenseInfo{} },
+	ResourceLoadBalancer:      func() interface{} { return &sacloud.LoadBalancer{} },
+	ResourceMobileGateway:     func() interface{} { return &sacloud.MobileGateway{} },
+	ResourceNFS:               func() interface{} { return &sacloud.NFS{} },
+	ResourceNote:              func() interface{} { return &sacloud.Note{} },
+	ResourcePacketFilter:      func() interface{} { return &sacloud.PacketFilter{} },
+	ResourcePrivateHost:       func() interface{} { return &sacloud.PrivateHost{} },
+	ResourcePrivateHostPlan:   func() interface{} { return &sacloud.PrivateHostPlan{} },
+	ResourceProxyLB:           func() interface{} { return &sacloud.ProxyLB{} },
+	ResourceRegion:            func() interface{} { return &sacloud.Region{} },
+	ResourceServer:            func() interface{} { return &sacloud.Server{} },
+	ResourceServerPlan:        func() interface{} { return &sacloud.ServerPlan{} },
+	ResourceServiceClass:      func() interface{} { return &sacloud.ServiceClass{} },
+	ResourceSIM:               func() interface{} { return &sacloud.SIM{} },
+	ResourceSimpleMonitor:     func() interface{} { return &sacloud.SimpleMonitor{} },
+	ResourceSubnet:            func() interface{} { return &sacloud.Subnet{} },
+	ResourceSSHKey:            func() interface{} { return &sacloud.SSHKey{} },
+	ResourceSwitch:            func() interface{} { return &sacloud.Switch{} },
+	ResourceVPCRouter:         func() interface{} { return &sacloud.VPCRouter{} },
+	ResourceWebAccel:          func() interface{} { return &sacloud.WebAccel{} },
+	ResourceZone:              func() interface{} { return &sacloud.Zone{} },
 
 	valuePoolResourceKey:         func() interface{} { return &valuePool{} },
 	"BillDetails":                func() interface{} { return &[]*sacloud.BillDetail{} },
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/ops_local_router.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/ops_local_router.go
new file mode 100644
index 00000000..c69be91a
--- /dev/null
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/ops_local_router.go
@@ -0,0 +1,211 @@
+// Copyright 2016-2020 The Libsacloud Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fake
+
+import (
+	"context"
+	"fmt"
+	"net"
+	"time"
+
+	"github.com/sacloud/libsacloud/v2/sacloud"
+	"github.com/sacloud/libsacloud/v2/sacloud/types"
+)
+
+// Find is fake implementation
+func (o *LocalRouterOp) Find(ctx context.Context, conditions *sacloud.FindCondition) (*sacloud.LocalRouterFindResult, error) {
+	results, _ := find(o.key, sacloud.APIDefaultZone, conditions)
+	var values []*sacloud.LocalRouter
+	for _, res := range results {
+		dest := &sacloud.LocalRouter{}
+		copySameNameField(res, dest)
+		values = append(values, dest)
+	}
+	return &sacloud.LocalRouterFindResult{
+		Total:        len(results),
+		Count:        len(results),
+		From:         0,
+		LocalRouters: values,
+	}, nil
+}
+
+// Create is fake implementation
+func (o *LocalRouterOp) Create(ctx context.Context, param *sacloud.LocalRouterCreateRequest) (*sacloud.LocalRouter, error) {
+	result := &sacloud.LocalRouter{}
+	copySameNameField(param, result)
+	fill(result, fillID, fillCreatedAt)
+
+	result.Availability = types.Availabilities.Available
+	result.SecretKeys = []string{"dummy"}
+
+	status := &sacloud.LocalRouterHealth{
+		Peers: []*sacloud.LocalRouterHealthPeer{},
+	}
+	ds().Put(ResourceLocalRouter+"Status", sacloud.APIDefaultZone, result.ID, status)
+
+	putLocalRouter(sacloud.APIDefaultZone, result)
+	return result, nil
+}
+
+// Read is fake implementation
+func (o *LocalRouterOp) Read(ctx context.Context, id types.ID) (*sacloud.LocalRouter, error) {
+	value := getLocalRouterByID(sacloud.APIDefaultZone, id)
+	if value == nil {
+		return nil, newErrorNotFound(o.key, id)
+	}
+	dest := &sacloud.LocalRouter{}
+	copySameNameField(value, dest)
+	return dest, nil
+}
+
+// Update is fake implementation
+func (o *LocalRouterOp) Update(ctx context.Context, id types.ID, param *sacloud.LocalRouterUpdateRequest) (*sacloud.LocalRouter, error) {
+	value, err := o.Read(ctx, id)
+	if err != nil {
+		return nil, err
+	}
+	copySameNameField(param, value)
+	fill(value, fillModifiedAt)
+
+	status := &sacloud.LocalRouterHealth{
+		Peers: []*sacloud.LocalRouterHealthPeer{},
+	}
+	for _, peer := range value.Peers {
+		p, err := o.Read(ctx, peer.ID)
+		if err != nil {
+			return nil, err
+		}
+		var routes []string
+		if p.Interface != nil {
+			_, ipNet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", p.Interface.VirtualIPAddress, p.Interface.NetworkMaskLen))
+			if err != nil {
+				return nil, err
+			}
+			if ipNet != nil {
+				routes = append(routes, ipNet.String())
+			}
+
+			for _, sr := range p.StaticRoutes {
+				routes = append(routes, sr.Prefix)
+			}
+		}
+
+		status.Peers = append(status.Peers, &sacloud.LocalRouterHealthPeer{
+			ID:     peer.ID,
+			Status: types.ServerInstanceStatuses.Up,
+			Routes: routes,
+		})
+	}
+
+	ds().Put(ResourceLocalRouter+"Status", sacloud.APIDefaultZone, value.ID, status)
+
+	putLocalRouter(sacloud.APIDefaultZone, value)
+	return value, nil
+}
+
+// UpdateSettings is fake implementation
+func (o *LocalRouterOp) UpdateSettings(ctx context.Context, id types.ID, param *sacloud.LocalRouterUpdateSettingsRequest) (*sacloud.LocalRouter, error) {
+	value, err := o.Read(ctx, id)
+	if err != nil {
+		return nil, err
+	}
+	copySameNameField(param, value)
+	fill(value, fillModifiedAt)
+
+	status := &sacloud.LocalRouterHealth{
+		Peers: []*sacloud.LocalRouterHealthPeer{},
+	}
+	for _, peer := range value.Peers {
+		p, err := o.Read(ctx, peer.ID)
+		if err != nil {
+			return nil, err
+		}
+		var routes []string
+		if p.Interface != nil {
+			_, ipNet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", p.Interface.VirtualIPAddress, p.Interface.NetworkMaskLen))
+			if err != nil {
+				return nil, err
+			}
+			if ipNet != nil {
+				routes = append(routes, ipNet.String())
+			}
+
+			for _, sr := range p.StaticRoutes {
+				routes = append(routes, sr.Prefix)
+			}
+		}
+
+		status.Peers = append(status.Peers, &sacloud.LocalRouterHealthPeer{
+			ID:     peer.ID,
+			Status: types.ServerInstanceStatuses.Up,
+			Routes: routes,
+		})
+	}
+
+	ds().Put(ResourceLocalRouter+"Status", sacloud.APIDefaultZone, value.ID, status)
+
+	putLocalRouter(sacloud.APIDefaultZone, value)
+	return value, nil
+}
+
+// Delete is fake implementation
+func (o *LocalRouterOp) Delete(ctx context.Context, id types.ID) error {
+	_, err := o.Read(ctx, id)
+	if err != nil {
+		return err
+	}
+
+	ds().Delete(o.key, sacloud.APIDefaultZone, id)
+	return nil
+}
+
+// HealthStatus is fake implementation
+func (o *LocalRouterOp) HealthStatus(ctx context.Context, id types.ID) (*sacloud.LocalRouterHealth, error) {
+	_, err := o.Read(ctx, id)
+	if err != nil {
+		return nil, err
+	}
+
+	result := ds().Get(ResourceLocalRouter+"Status", sacloud.APIDefaultZone, id)
+	if result == nil {
+		return nil, newErrorNotFound(o.key, id)
+	}
+	return result.(*sacloud.LocalRouterHealth), nil
+}
+
+// MonitorLocalRouter is fake implementation
+func (o *LocalRouterOp) MonitorLocalRouter(ctx context.Context, id types.ID, condition *sacloud.MonitorCondition) (*sacloud.LocalRouterActivity, error) {
+	_, err := o.Read(ctx, id)
+	if err != nil {
+		return nil, err
+	}
+
+	now := time.Now().Truncate(time.Second)
+	m := now.Minute() % 5
+	if m != 0 {
+		now.Add(time.Duration(m) * time.Minute)
+	}
+
+	res := &sacloud.LocalRouterActivity{}
+	for i := 0; i < 5; i++ {
+		res.Values = append(res.Values, &sacloud.MonitorLocalRouterValue{
+			Time:               now.Add(time.Duration(i*-5) * time.Minute),
+			ReceiveBytesPerSec: float64(random(1000)),
+			SendBytesPerSec:    float64(random(1000)),
+		})
+	}
+
+	return res, nil
+}
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_api_ops.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_api_ops.go
index 6794e215..6552237f 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_api_ops.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_api_ops.go
@@ -91,6 +91,9 @@ func SwitchFactoryFuncToFake() {
 	sacloud.SetClientFactoryFunc(ResourceLoadBalancer, func(caller sacloud.APICaller) interface{} {
 		return NewLoadBalancerOp()
 	})
+	sacloud.SetClientFactoryFunc(ResourceLocalRouter, func(caller sacloud.APICaller) interface{} {
+		return NewLocalRouterOp()
+	})
 	sacloud.SetClientFactoryFunc(ResourceMobileGateway, func(caller sacloud.APICaller) interface{} {
 		return NewMobileGatewayOp()
 	})
@@ -518,6 +521,22 @@ func NewLoadBalancerOp() sacloud.LoadBalancerAPI {
 	}
 }
 
+/*************************************************
+* LocalRouterOp
+*************************************************/
+
+// LocalRouterOp is fake implementation of LocalRouterAPI interface
+type LocalRouterOp struct {
+	key string
+}
+
+// NewLocalRouterOp creates new LocalRouterOp instance
+func NewLocalRouterOp() sacloud.LocalRouterAPI {
+	return &LocalRouterOp{
+		key: ResourceLocalRouter,
+	}
+}
+
 /*************************************************
 * MobileGatewayOp
 *************************************************/
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_resources.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_resources.go
index a11688eb..a62fa6ac 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_resources.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_resources.go
@@ -63,6 +63,8 @@ const (
 	ResourceLicenseInfo = "LicenseInfo"
 	// ResourceLoadBalancer is resource key of fake store
 	ResourceLoadBalancer = "LoadBalancer"
+	// ResourceLocalRouter is resource key of fake store
+	ResourceLocalRouter = "LocalRouter"
 	// ResourceMobileGateway is resource key of fake store
 	ResourceMobileGateway = "MobileGateway"
 	// ResourceNFS is resource key of fake store
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_store.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_store.go
index bc721fae..663f325a 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_store.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/fake/zz_store.go
@@ -666,6 +666,34 @@ func putLoadBalancer(zone string, value *sacloud.LoadBalancer) {
 	ds().Put(ResourceLoadBalancer, zone, 0, value)
 }
 
+func getLocalRouter(zone string) []*sacloud.LocalRouter {
+	values := ds().List(ResourceLocalRouter, zone)
+	var ret []*sacloud.LocalRouter
+	for _, v := range values {
+		if v, ok := v.(*sacloud.LocalRouter); ok {
+			ret = append(ret, v)
+		}
+	}
+	return ret
+}
+
+func getLocalRouterByID(zone string, id types.ID) *sacloud.LocalRouter {
+	v := ds().Get(ResourceLocalRouter, zone, id)
+	if v, ok := v.(*sacloud.LocalRouter); ok {
+		return v
+	}
+	return nil
+}
+
+func putLocalRouter(zone string, value *sacloud.LocalRouter) {
+	var v interface{} = value
+	if id, ok := v.(accessor.ID); ok {
+		ds().Put(ResourceLocalRouter, zone, id.GetID(), value)
+		return
+	}
+	ds().Put(ResourceLocalRouter, zone, 0, value)
+}
+
 func getMobileGateway(zone string) []*sacloud.MobileGateway {
 	values := ds().List(ResourceMobileGateway, zone)
 	var ret []*sacloud.MobileGateway
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/hybrid_connection.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/hybrid_connection.go
new file mode 100644
index 00000000..7c19324b
--- /dev/null
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/hybrid_connection.go
@@ -0,0 +1,32 @@
+// Copyright 2016-2020 The Libsacloud Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package naked
+
+import "github.com/sacloud/libsacloud/v2/sacloud/types"
+
+// HybridConnection ハイブリッドコネクション
+type HybridConnection struct {
+	ID       types.ID
+	Services []*HybridConnectionService
+}
+
+// HybridConnectionService ハイブリッドコネクションにて接続されているサービスの情報
+type HybridConnectionService struct {
+	ServiceCategory string
+	ServiceName     string
+	ServiceCode     string
+	CloudZone       string
+	IsMyself        bool
+}
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/local_router.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/local_router.go
new file mode 100644
index 00000000..900e68fb
--- /dev/null
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/local_router.go
@@ -0,0 +1,102 @@
+// Copyright 2016-2020 The Libsacloud Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package naked
+
+import (
+	"time"
+
+	"github.com/sacloud/libsacloud/v2/sacloud/types"
+)
+
+// LocalRouter ローカルルータ
+type LocalRouter struct {
+	ID           types.ID             `json:",omitempty" yaml:"id,omitempty" structs:",omitempty"`
+	Name         string               `json:",omitempty" yaml:"name,omitempty" structs:",omitempty"`
+	Description  string               `yaml:"description"`
+	Tags         types.Tags           `yaml:"tags"`
+	Icon         *Icon                `json:",omitempty" yaml:"icon,omitempty" structs:",omitempty"`
+	CreatedAt    *time.Time           `json:",omitempty" yaml:"created_at,omitempty" structs:",omitempty"`
+	ModifiedAt   *time.Time           `json:",omitempty" yaml:"modified_at,omitempty" structs:",omitempty"`
+	Availability types.EAvailability  `json:",omitempty" yaml:"availability,omitempty" structs:",omitempty"`
+	Provider     *Provider            `json:",omitempty" yaml:"provider,omitempty" structs:",omitempty"`
+	Settings     *LocalRouterSettings `json:",omitempty" yaml:"settings,omitempty" structs:",omitempty"`
+	SettingsHash string               `json:",omitempty" yaml:"settings_hash,omitempty" structs:",omitempty"`
+	Status       *LocalRouterStatus   `json:",omitempty" yaml:"status" structs:",omitempty"`
+	ServiceClass string               `json:",omitempty" yaml:"service_class,omitempty" structs:",omitempty"`
+}
+
+// LocalRouterSettings セッティング
+type LocalRouterSettings struct {
+	LocalRouter *LocalRouterSetting `json:",omitempty" yaml:"local_router,omitempty" structs:",omitempty"`
+}
+
+// LocalRouterSettingsUpdate ローカルルータ セッティング更新
+type LocalRouterSettingsUpdate struct {
+	Settings     *LocalRouterSettings `json:",omitempty" yaml:"settings,omitempty" structs:",omitempty"`
+	SettingsHash string               `json:",omitempty" yaml:"settings_hash,omitempty" structs:",omitempty"`
+}
+
+// LocalRouterSetting セッティング
+type LocalRouterSetting struct {
+	Switch       *LocalRouterSettingSwitch        `yaml:"switch"`
+	Interface    *LocalRouterSettingInterface     `yaml:"interface"`
+	Peers        []*LocalRouterSettingPeer        `yaml:"peers"`
+	StaticRoutes []*LocalRouterSettingStaticRoute `yaml:"static_routes"`
+}
+
+// LocalRouterSettingSwitch ローカルルータのスイッチ設定
+type LocalRouterSettingSwitch struct {
+	Code     string `json:",omitempty" yaml:",omitempty" structs:",omitempty"` // リソースIDなど
+	Category string `json:",omitempty" yaml:",omitempty" structs:",omitempty"` // cloud/vps/専用サーバなどを表す
+	ZoneID   string `json:",omitempty" yaml:",omitempty" structs:",omitempty"` // クラウドの場合is1aなど Note: VPSの場合は数値型となる
+}
+
+// LocalRouterSettingInterface インターフェース設定
+type LocalRouterSettingInterface struct {
+	VirtualIPAddress string   `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
+	IPAddress        []string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
+	NetworkMaskLen   int      `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
+	VRID             int      `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
+}
+
+// LocalRouterSettingPeer ピア設定
+type LocalRouterSettingPeer struct {
+	ID          string `yaml:"id"` // 文字列でないとエラーになるためtypes.IDではなくstringとする
+	SecretKey   string `yaml:"secret_key"`
+	Enabled     bool   `yaml:"enabled"`
+	Description string `yaml:"description"`
+}
+
+// LocalRouterSettingStaticRoute スタティックルート
+type LocalRouterSettingStaticRoute struct {
+	Prefix  string `yaml:"prefix"`
+	NextHop string `yaml:"next_hop"`
+}
+
+// LocalRouterStatus ステータス
+type LocalRouterStatus struct {
+	SecretKeys []string `json:",omitempty" yaml:",omitempty" structs:",omitempty"`
+}
+
+// LocalRouterHealth ローカルルータのヘルスチェック結果
+type LocalRouterHealth struct {
+	LocalRouter *struct {
+		Peers []*struct {
+			ID     types.ID
+			Status types.EServerInstanceStatus
+			Routes []string
+		} `yaml:"peers"`
+	}
+}
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/monitor.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/monitor.go
index 6c45073a..b3e7a1d0 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/monitor.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/monitor.go
@@ -41,6 +41,8 @@ type MonitorValues struct {
 	Link MonitorLinkValues
 	// Connection 接続数
 	Connection MonitorConnectionValues
+	// LocalRouter Receive/Send bytes per sec
+	LocalRouter MonitorLocalRouterValues
 }
 
 // UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
@@ -128,6 +130,14 @@ func (m *MonitorValues) UnmarshalJSON(data []byte) error {
 		return nil
 	}
 
+	//	LocalRouter
+	if err := json.Unmarshal(data, &v.LocalRouter); err != nil {
+		return nil
+	}
+	if len(v.LocalRouter) > 0 {
+		*m = v
+		return nil
+	}
 	return nil
 }
 
@@ -440,29 +450,65 @@ func (m *MonitorConnectionValues) UnmarshalJSON(data []byte) error {
 	return nil
 }
 
+/************************************************
+ * LocalRouter(Receive/Send BytesPerSec)
+************************************************/
+
+// MonitorLocalRouterValue アクティビティモニタ
+type MonitorLocalRouterValue struct {
+	Time               time.Time
+	ReceiveBytesPerSec float64
+	SendBytesPerSec    float64
+}
+
+// MonitorRouterValues アクティビティモニタ
+type MonitorLocalRouterValues []*MonitorLocalRouterValue
+
+// UnmarshalJSON アクティビティモニタ向けUnmarshalJSON実装
+func (m *MonitorLocalRouterValues) UnmarshalJSON(data []byte) error {
+	targetData := strings.Replace(strings.Replace(string(data), " ", "", -1), "\n", "", -1)
+	if targetData == `[]` {
+		return nil
+	}
+
+	var rawMonitorValues rawMonitorValues
+	if err := json.Unmarshal(data, &rawMonitorValues); err != nil {
+		return err
+	}
+	values, err := rawMonitorValues.monitorLocalRouterValues()
+	if err != nil {
+		return err
+	}
+
+	*m = values
+	return nil
+}
+
 // rawMonitorValue アクティビティモニター
 type rawMonitorValue struct {
-	CPUTime           *float64 `json:"CPU-TIME,omitempty" yaml:"cpu_timme,omitempty" structs:",omitempty"`                     // CPU時間
-	Write             *float64 `json:",omitempty" yaml:"write,omitempty" structs:",omitempty"`                                 // ディスク書き込み
-	Read              *float64 `json:",omitempty" yaml:"read,omitempty" structs:",omitempty"`                                  // ディスク読み取り
-	Receive           *float64 `json:",omitempty" yaml:"receive,omitempty" structs:",omitempty"`                               // パケット受信
-	Send              *float64 `json:",omitempty" yaml:"send,omitempty" structs:",omitempty"`                                  // パケット送信
-	In                *float64 `json:",omitempty" yaml:"in,omitempty" structs:",omitempty"`                                    // パケット受信
-	Out               *float64 `json:",omitempty" yaml:"out,omitempty" structs:",omitempty"`                                   // パケット送信
-	TotalMemorySize   *float64 `json:"Total-Memory-Size,omitempty" yaml:"total_memory_size,omitempty" structs:",omitempty"`    // 総メモリサイズ
-	UsedMemorySize    *float64 `json:"Used-Memory-Size,omitempty" yaml:"used_memory_size,omitempty" structs:",omitempty"`      // 使用済みメモリサイズ
-	TotalDisk1Size    *float64 `json:"Total-Disk1-Size,omitempty" yaml:"total_disk1_size,omitempty" structs:",omitempty"`      // 総ディスクサイズ
-	UsedDisk1Size     *float64 `json:"Used-Disk1-Size,omitempty" yaml:"used_disk1_size,omitempty" structs:",omitempty"`        // 使用済みディスクサイズ
-	TotalDisk2Size    *float64 `json:"Total-Disk2-Size,omitempty" yaml:"total_disk2_size,omitempty" structs:",omitempty"`      // 総ディスクサイズ
-	UsedDisk2Size     *float64 `json:"Used-Disk2-Size,omitempty" yaml:"used_disk2_size,omitempty" structs:",omitempty"`        // 使用済みディスクサイズ
-	BinlogUsedSizeKiB *float64 `json:"binlogUsedSizeKiB,omitempty" yaml:"binlog_used_size_kib,omitempty" structs:",omitempty"` // バイナリログのサイズ(レプリケーション有効時のみ、master/slave両方で利用可能)
-	DelayTimeSec      *float64 `json:"delayTimeSec,omitempty" yaml:"delay_time_sec,omitempty" structs:",omitempty"`            // レプリケーション遅延時間(レプリケーション有効時のみ、slave側のみ)
-	FreeDiskSize      *float64 `json:"Free-Disk-Size,omitempty" yaml:"free_disk_size,omitempty" structs:",omitempty"`          // 空きディスクサイズ(NFS)
-	ResponseTimeSec   *float64 `json:"responsetimesec,omitempty" yaml:"response_time_sec,omitempty" structs:",omitempty"`      // レスポンスタイム(シンプル監視)
-	UplinkBPS         *float64 `json:"UplinkBps,omitempty" yaml:"uplink_bps,omitempty" structs:",omitempty"`                   // 上り方向トラフィック
-	DownlinkBPS       *float64 `json:"DownlinkBps,omitempty" yaml:"downlink_bps,omitempty" structs:",omitempty"`               // 下り方向トラフィック
-	ActiveConnections *float64 `json:"activeConnections,omitempty" yaml:"active_connections,omitempty" structs:",omitempty"`   // アクティブコネクション(プロキシLB)
-	ConnectionsPerSec *float64 `json:"connectionsPerSec,omitempty" yaml:"connections_per_sec,omitempty" structs:",omitempty"`  // 秒間コネクション数
+	CPUTime            *float64 `json:"CPU-TIME,omitempty" yaml:"cpu_timme,omitempty" structs:",omitempty"`                       // CPU時間
+	Write              *float64 `json:",omitempty" yaml:"write,omitempty" structs:",omitempty"`                                   // ディスク書き込み
+	Read               *float64 `json:",omitempty" yaml:"read,omitempty" structs:",omitempty"`                                    // ディスク読み取り
+	Receive            *float64 `json:",omitempty" yaml:"receive,omitempty" structs:",omitempty"`                                 // パケット受信
+	Send               *float64 `json:",omitempty" yaml:"send,omitempty" structs:",omitempty"`                                    // パケット送信
+	In                 *float64 `json:",omitempty" yaml:"in,omitempty" structs:",omitempty"`                                      // パケット受信
+	Out                *float64 `json:",omitempty" yaml:"out,omitempty" structs:",omitempty"`                                     // パケット送信
+	TotalMemorySize    *float64 `json:"Total-Memory-Size,omitempty" yaml:"total_memory_size,omitempty" structs:",omitempty"`      // 総メモリサイズ
+	UsedMemorySize     *float64 `json:"Used-Memory-Size,omitempty" yaml:"used_memory_size,omitempty" structs:",omitempty"`        // 使用済みメモリサイズ
+	TotalDisk1Size     *float64 `json:"Total-Disk1-Size,omitempty" yaml:"total_disk1_size,omitempty" structs:",omitempty"`        // 総ディスクサイズ
+	UsedDisk1Size      *float64 `json:"Used-Disk1-Size,omitempty" yaml:"used_disk1_size,omitempty" structs:",omitempty"`          // 使用済みディスクサイズ
+	TotalDisk2Size     *float64 `json:"Total-Disk2-Size,omitempty" yaml:"total_disk2_size,omitempty" structs:",omitempty"`        // 総ディスクサイズ
+	UsedDisk2Size      *float64 `json:"Used-Disk2-Size,omitempty" yaml:"used_disk2_size,omitempty" structs:",omitempty"`          // 使用済みディスクサイズ
+	BinlogUsedSizeKiB  *float64 `json:"binlogUsedSizeKiB,omitempty" yaml:"binlog_used_size_kib,omitempty" structs:",omitempty"`   // バイナリログのサイズ(レプリケーション有効時のみ、master/slave両方で利用可能)
+	DelayTimeSec       *float64 `json:"delayTimeSec,omitempty" yaml:"delay_time_sec,omitempty" structs:",omitempty"`              // レプリケーション遅延時間(レプリケーション有効時のみ、slave側のみ)
+	FreeDiskSize       *float64 `json:"Free-Disk-Size,omitempty" yaml:"free_disk_size,omitempty" structs:",omitempty"`            // 空きディスクサイズ(NFS)
+	ResponseTimeSec    *float64 `json:"responsetimesec,omitempty" yaml:"response_time_sec,omitempty" structs:",omitempty"`        // レスポンスタイム(シンプル監視)
+	UplinkBPS          *float64 `json:"UplinkBps,omitempty" yaml:"uplink_bps,omitempty" structs:",omitempty"`                     // 上り方向トラフィック
+	DownlinkBPS        *float64 `json:"DownlinkBps,omitempty" yaml:"downlink_bps,omitempty" structs:",omitempty"`                 // 下り方向トラフィック
+	ActiveConnections  *float64 `json:"activeConnections,omitempty" yaml:"active_connections,omitempty" structs:",omitempty"`     // アクティブコネクション(プロキシLB)
+	ConnectionsPerSec  *float64 `json:"connectionsPerSec,omitempty" yaml:"connections_per_sec,omitempty" structs:",omitempty"`    // 秒間コネクション数
+	ReceiveBytesPerSec *float64 `json:"receiveBytesPerSec,omitempty" yaml:"receive_bytes_per_sec,omitempty" structs:",omitempty"` // 秒間受信バイト数
+	SendBytesPerSec    *float64 `json:"sendBytesPerSec,omitempty" yaml:"send_bytes_per_sec,omitempty" structs:",omitempty"`       // 秒間送信バイト数
 }
 
 // UnmarshalJSON JSONアンマーシャル(配列、オブジェクトが混在するためここで対応)
@@ -473,27 +519,29 @@ func (m *rawMonitorValue) UnmarshalJSON(data []byte) error {
 	}
 
 	tmp := &struct {
-		CPUTime           *float64 `json:"CPU-TIME,omitempty" yaml:"cpu_timme,omitempty" structs:",omitempty"`
-		Write             *float64 `json:",omitempty" yaml:"write,omitempty" structs:",omitempty"`
-		Read              *float64 `json:",omitempty" yaml:"read,omitempty" structs:",omitempty"`
-		Receive           *float64 `json:",omitempty" yaml:"receive,omitempty" structs:",omitempty"`
-		Send              *float64 `json:",omitempty" yaml:"send,omitempty" structs:",omitempty"`
-		In                *float64 `json:",omitempty" yaml:"in,omitempty" structs:",omitempty"`
-		Out               *float64 `json:",omitempty" yaml:"out,omitempty" structs:",omitempty"`
-		TotalMemorySize   *float64 `json:"Total-Memory-Size,omitempty" yaml:"total_memory_size,omitempty" structs:",omitempty"`
-		UsedMemorySize    *float64 `json:"Used-Memory-Size,omitempty" yaml:"used_memory_size,omitempty" structs:",omitempty"`
-		TotalDisk1Size    *float64 `json:"Total-Disk1-Size,omitempty" yaml:"total_disk1_size,omitempty" structs:",omitempty"`
-		UsedDisk1Size     *float64 `json:"Used-Disk1-Size,omitempty" yaml:"used_disk1_size,omitempty" structs:",omitempty"`
-		TotalDisk2Size    *float64 `json:"Total-Disk2-Size,omitempty" yaml:"total_disk2_size,omitempty" structs:",omitempty"`
-		UsedDisk2Size     *float64 `json:"Used-Disk2-Size,omitempty" yaml:"used_disk2_size,omitempty" structs:",omitempty"`
-		BinlogUsedSizeKiB *float64 `json:"binlogUsedSizeKiB,omitempty" yaml:"binlog_used_size_kib,omitempty" structs:",omitempty"`
-		DelayTimeSec      *float64 `json:"delayTimeSec,omitempty" yaml:"delay_time_sec,omitempty" structs:",omitempty"`
-		FreeDiskSize      *float64 `json:"Free-Disk-Size,omitempty" yaml:"free_disk_size,omitempty" structs:",omitempty"`
-		ResponseTimeSec   *float64 `json:"responsetimesec,omitempty" yaml:"response_time_sec,omitempty" structs:",omitempty"`
-		UplinkBPS         *float64 `json:"UplinkBps,omitempty" yaml:"uplink_bps,omitempty" structs:",omitempty"`
-		DownlinkBPS       *float64 `json:"DownlinkBps,omitempty" yaml:"downlink_bps,omitempty" structs:",omitempty"`
-		ActiveConnections *float64 `json:"activeConnections,omitempty" yaml:"active_connections,omitempty" structs:",omitempty"`
-		ConnectionsPerSec *float64 `json:"connectionsPerSec,omitempty" yaml:"connections_per_sec,omitempty" structs:",omitempty"`
+		CPUTime            *float64 `json:"CPU-TIME,omitempty" yaml:"cpu_timme,omitempty" structs:",omitempty"`
+		Write              *float64 `json:",omitempty" yaml:"write,omitempty" structs:",omitempty"`
+		Read               *float64 `json:",omitempty" yaml:"read,omitempty" structs:",omitempty"`
+		Receive            *float64 `json:",omitempty" yaml:"receive,omitempty" structs:",omitempty"`
+		Send               *float64 `json:",omitempty" yaml:"send,omitempty" structs:",omitempty"`
+		In                 *float64 `json:",omitempty" yaml:"in,omitempty" structs:",omitempty"`
+		Out                *float64 `json:",omitempty" yaml:"out,omitempty" structs:",omitempty"`
+		TotalMemorySize    *float64 `json:"Total-Memory-Size,omitempty" yaml:"total_memory_size,omitempty" structs:",omitempty"`
+		UsedMemorySize     *float64 `json:"Used-Memory-Size,omitempty" yaml:"used_memory_size,omitempty" structs:",omitempty"`
+		TotalDisk1Size     *float64 `json:"Total-Disk1-Size,omitempty" yaml:"total_disk1_size,omitempty" structs:",omitempty"`
+		UsedDisk1Size      *float64 `json:"Used-Disk1-Size,omitempty" yaml:"used_disk1_size,omitempty" structs:",omitempty"`
+		TotalDisk2Size     *float64 `json:"Total-Disk2-Size,omitempty" yaml:"total_disk2_size,omitempty" structs:",omitempty"`
+		UsedDisk2Size      *float64 `json:"Used-Disk2-Size,omitempty" yaml:"used_disk2_size,omitempty" structs:",omitempty"`
+		BinlogUsedSizeKiB  *float64 `json:"binlogUsedSizeKiB,omitempty" yaml:"binlog_used_size_kib,omitempty" structs:",omitempty"`
+		DelayTimeSec       *float64 `json:"delayTimeSec,omitempty" yaml:"delay_time_sec,omitempty" structs:",omitempty"`
+		FreeDiskSize       *float64 `json:"Free-Disk-Size,omitempty" yaml:"free_disk_size,omitempty" structs:",omitempty"`
+		ResponseTimeSec    *float64 `json:"responsetimesec,omitempty" yaml:"response_time_sec,omitempty" structs:",omitempty"`
+		UplinkBPS          *float64 `json:"UplinkBps,omitempty" yaml:"uplink_bps,omitempty" structs:",omitempty"`
+		DownlinkBPS        *float64 `json:"DownlinkBps,omitempty" yaml:"downlink_bps,omitempty" structs:",omitempty"`
+		ActiveConnections  *float64 `json:"activeConnections,omitempty" yaml:"active_connections,omitempty" structs:",omitempty"`
+		ConnectionsPerSec  *float64 `json:"connectionsPerSec,omitempty" yaml:"connections_per_sec,omitempty" structs:",omitempty"`
+		ReceiveBytesPerSec *float64 `json:"receiveBytesPerSec,omitempty" yaml:"receive_bytes_per_sec,omitempty" structs:",omitempty"`
+		SendBytesPerSec    *float64 `json:"sendBytesPerSec,omitempty" yaml:"send_bytes_per_sec,omitempty" structs:",omitempty"`
 	}{}
 	if err := json.Unmarshal(data, &tmp); err != nil {
 		return err
@@ -520,6 +568,8 @@ func (m *rawMonitorValue) UnmarshalJSON(data []byte) error {
 	m.DownlinkBPS = tmp.DownlinkBPS
 	m.ActiveConnections = tmp.ActiveConnections
 	m.ConnectionsPerSec = tmp.ConnectionsPerSec
+	m.ReceiveBytesPerSec = tmp.ReceiveBytesPerSec
+	m.SendBytesPerSec = tmp.SendBytesPerSec
 
 	return nil
 }
@@ -729,3 +779,25 @@ func (m *rawMonitorValues) monitorConnectionValues() (MonitorConnectionValues, e
 	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
 	return values, nil
 }
+
+func (m *rawMonitorValues) monitorLocalRouterValues() (MonitorLocalRouterValues, error) {
+	var values MonitorLocalRouterValues
+
+	for k, v := range *m {
+		if v.ReceiveBytesPerSec == nil || v.SendBytesPerSec == nil {
+			continue
+		}
+		time, err := time.Parse(time.RFC3339, k) // RFC3339 ≒ ISO8601
+		if err != nil {
+			return nil, err
+		}
+		values = append(values, &MonitorLocalRouterValue{
+			Time:               time,
+			ReceiveBytesPerSec: *v.ReceiveBytesPerSec,
+			SendBytesPerSec:    *v.SendBytesPerSec,
+		})
+	}
+
+	sort.Slice(values, func(i, j int) bool { return values[i].Time.Before(values[j].Time) })
+	return values, nil
+}
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/switch.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/switch.go
index 697405b3..8386bb3a 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/switch.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/naked/switch.go
@@ -22,20 +22,21 @@ import (
 
 // Switch スイッチ
 type Switch struct {
-	ID          types.ID     `json:",omitempty" yaml:"id,omitempty" structs:",omitempty"`
-	Name        string       `json:",omitempty" yaml:"name,omitempty" structs:",omitempty"`
-	Description string       `yaml:"description"`
-	Tags        types.Tags   `yaml:"tags"`
-	Icon        *Icon        `json:",omitempty" yaml:"icon,omitempty" structs:",omitempty"`
-	CreatedAt   *time.Time   `json:",omitempty" yaml:"created_at,omitempty" structs:",omitempty"`
-	ModifiedAt  *time.Time   `json:",omitempty" yaml:"modified_at,omitempty" structs:",omitempty"`
-	Scope       types.EScope `json:",omitempty" yaml:"scope,omitempty" structs:",omitempty"`
-	Subnet      *Subnet      `json:",omitempty" yaml:"subnet,omitempty" structs:",omitempty"`
-	UserSubnet  *UserSubnet  `json:",omitempty" yaml:"user_subnet,omitempty" structs:",omitempty"`
-	Zone        *Zone        `json:",omitempty" yaml:"zone,omitempty" structs:",omitempty"`
-	Internet    *Internet    `json:",omitempty" yaml:"internet,omitempty" structs:",omitempty"`
-	Subnets     []*Subnet    `json:",omitempty" yaml:"subnets,omitempty" structs:",omitempty"`
-	IPv6Nets    []*IPv6Net   `json:",omitempty" yaml:"ipv6nets,omitempty" structs:",omitempty"`
-	Bridge      *Bridge      `json:",omitempty" yaml:"bridge,omitempty" structs:",omitempty"`
-	ServerCount int          `json:",omitempty" yaml:"server_count,omitempty" structs:",omitempty"`
+	ID               types.ID          `json:",omitempty" yaml:"id,omitempty" structs:",omitempty"`
+	Name             string            `json:",omitempty" yaml:"name,omitempty" structs:",omitempty"`
+	Description      string            `yaml:"description"`
+	Tags             types.Tags        `yaml:"tags"`
+	Icon             *Icon             `json:",omitempty" yaml:"icon,omitempty" structs:",omitempty"`
+	CreatedAt        *time.Time        `json:",omitempty" yaml:"created_at,omitempty" structs:",omitempty"`
+	ModifiedAt       *time.Time        `json:",omitempty" yaml:"modified_at,omitempty" structs:",omitempty"`
+	Scope            types.EScope      `json:",omitempty" yaml:"scope,omitempty" structs:",omitempty"`
+	Subnet           *Subnet           `json:",omitempty" yaml:"subnet,omitempty" structs:",omitempty"`
+	UserSubnet       *UserSubnet       `json:",omitempty" yaml:"user_subnet,omitempty" structs:",omitempty"`
+	Zone             *Zone             `json:",omitempty" yaml:"zone,omitempty" structs:",omitempty"`
+	Internet         *Internet         `json:",omitempty" yaml:"internet,omitempty" structs:",omitempty"`
+	Subnets          []*Subnet         `json:",omitempty" yaml:"subnets,omitempty" structs:",omitempty"`
+	IPv6Nets         []*IPv6Net        `json:",omitempty" yaml:"ipv6nets,omitempty" structs:",omitempty"`
+	Bridge           *Bridge           `json:",omitempty" yaml:"bridge,omitempty" structs:",omitempty"`
+	ServerCount      int               `json:",omitempty" yaml:"server_count,omitempty" structs:",omitempty"`
+	HybridConnection *HybridConnection `json:",omitempty" yaml:"hybrid_connection,omitempty" structs:",omitempty"`
 }
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/trace/zz_api_tracer.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/trace/zz_api_tracer.go
index e810173a..368fd312 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/trace/zz_api_tracer.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/trace/zz_api_tracer.go
@@ -96,6 +96,9 @@ func AddClientFactoryHooks() {
 	sacloud.AddClientFacotyHookFunc("LoadBalancer", func(in interface{}) interface{} {
 		return NewLoadBalancerTracer(in.(sacloud.LoadBalancerAPI))
 	})
+	sacloud.AddClientFacotyHookFunc("LocalRouter", func(in interface{}) interface{} {
+		return NewLocalRouterTracer(in.(sacloud.LocalRouterAPI))
+	})
 	sacloud.AddClientFacotyHookFunc("MobileGateway", func(in interface{}) interface{} {
 		return NewMobileGatewayTracer(in.(sacloud.MobileGatewayAPI))
 	})
@@ -5261,6 +5264,274 @@ func (t *LoadBalancerTracer) Status(ctx context.Context, zone string, id types.I
 	return result, err
 }
 
+/*************************************************
+* LocalRouterTracer
+*************************************************/
+
+// LocalRouterTracer is for trace LocalRouterOp operations
+type LocalRouterTracer struct {
+	Internal sacloud.LocalRouterAPI
+}
+
+// NewLocalRouterTracer creates new LocalRouterTracer instance
+func NewLocalRouterTracer(in sacloud.LocalRouterAPI) sacloud.LocalRouterAPI {
+	return &LocalRouterTracer{
+		Internal: in,
+	}
+}
+
+// Find is API call with trace log
+func (t *LocalRouterTracer) Find(ctx context.Context, conditions *sacloud.FindCondition) (*sacloud.LocalRouterFindResult, error) {
+	log.Println("[TRACE] LocalRouterAPI.Find start")
+	targetArguments := struct {
+		Argconditions *sacloud.FindCondition `json:"conditions"`
+	}{
+		Argconditions: conditions,
+	}
+	if d, err := json.Marshal(targetArguments); err == nil {
+		log.Printf("[TRACE] \targs: %s\n", string(d))
+	}
+
+	defer func() {
+		log.Println("[TRACE] LocalRouterAPI.Find end")
+	}()
+
+	result, err := t.Internal.Find(ctx, conditions)
+	targetResults := struct {
+		Result *sacloud.LocalRouterFindResult
+		Error  error
+	}{
+		Result: result,
+		Error:  err,
+	}
+	if d, err := json.Marshal(targetResults); err == nil {
+		log.Printf("[TRACE] \tresults: %s\n", string(d))
+	}
+
+	return result, err
+}
+
+// Create is API call with trace log
+func (t *LocalRouterTracer) Create(ctx context.Context, param *sacloud.LocalRouterCreateRequest) (*sacloud.LocalRouter, error) {
+	log.Println("[TRACE] LocalRouterAPI.Create start")
+	targetArguments := struct {
+		Argparam *sacloud.LocalRouterCreateRequest `json:"param"`
+	}{
+		Argparam: param,
+	}
+	if d, err := json.Marshal(targetArguments); err == nil {
+		log.Printf("[TRACE] \targs: %s\n", string(d))
+	}
+
+	defer func() {
+		log.Println("[TRACE] LocalRouterAPI.Create end")
+	}()
+
+	resultLocalRouter, err := t.Internal.Create(ctx, param)
+	targetResults := struct {
+		LocalRouter *sacloud.LocalRouter
+		Error       error
+	}{
+		LocalRouter: resultLocalRouter,
+		Error:       err,
+	}
+	if d, err := json.Marshal(targetResults); err == nil {
+		log.Printf("[TRACE] \tresults: %s\n", string(d))
+	}
+
+	return resultLocalRouter, err
+}
+
+// Read is API call with trace log
+func (t *LocalRouterTracer) Read(ctx context.Context, id types.ID) (*sacloud.LocalRouter, error) {
+	log.Println("[TRACE] LocalRouterAPI.Read start")
+	targetArguments := struct {
+		Argid types.ID `json:"id"`
+	}{
+		Argid: id,
+	}
+	if d, err := json.Marshal(targetArguments); err == nil {
+		log.Printf("[TRACE] \targs: %s\n", string(d))
+	}
+
+	defer func() {
+		log.Println("[TRACE] LocalRouterAPI.Read end")
+	}()
+
+	resultLocalRouter, err := t.Internal.Read(ctx, id)
+	targetResults := struct {
+		LocalRouter *sacloud.LocalRouter
+		Error       error
+	}{
+		LocalRouter: resultLocalRouter,
+		Error:       err,
+	}
+	if d, err := json.Marshal(targetResults); err == nil {
+		log.Printf("[TRACE] \tresults: %s\n", string(d))
+	}
+
+	return resultLocalRouter, err
+}
+
+// Update is API call with trace log
+func (t *LocalRouterTracer) Update(ctx context.Context, id types.ID, param *sacloud.LocalRouterUpdateRequest) (*sacloud.LocalRouter, error) {
+	log.Println("[TRACE] LocalRouterAPI.Update start")
+	targetArguments := struct {
+		Argid    types.ID                          `json:"id"`
+		Argparam *sacloud.LocalRouterUpdateRequest `json:"param"`
+	}{
+		Argid:    id,
+		Argparam: param,
+	}
+	if d, err := json.Marshal(targetArguments); err == nil {
+		log.Printf("[TRACE] \targs: %s\n", string(d))
+	}
+
+	defer func() {
+		log.Println("[TRACE] LocalRouterAPI.Update end")
+	}()
+
+	resultLocalRouter, err := t.Internal.Update(ctx, id, param)
+	targetResults := struct {
+		LocalRouter *sacloud.LocalRouter
+		Error       error
+	}{
+		LocalRouter: resultLocalRouter,
+		Error:       err,
+	}
+	if d, err := json.Marshal(targetResults); err == nil {
+		log.Printf("[TRACE] \tresults: %s\n", string(d))
+	}
+
+	return resultLocalRouter, err
+}
+
+// UpdateSettings is API call with trace log
+func (t *LocalRouterTracer) UpdateSettings(ctx context.Context, id types.ID, param *sacloud.LocalRouterUpdateSettingsRequest) (*sacloud.LocalRouter, error) {
+	log.Println("[TRACE] LocalRouterAPI.UpdateSettings start")
+	targetArguments := struct {
+		Argid    types.ID                                  `json:"id"`
+		Argparam *sacloud.LocalRouterUpdateSettingsRequest `json:"param"`
+	}{
+		Argid:    id,
+		Argparam: param,
+	}
+	if d, err := json.Marshal(targetArguments); err == nil {
+		log.Printf("[TRACE] \targs: %s\n", string(d))
+	}
+
+	defer func() {
+		log.Println("[TRACE] LocalRouterAPI.UpdateSettings end")
+	}()
+
+	resultLocalRouter, err := t.Internal.UpdateSettings(ctx, id, param)
+	targetResults := struct {
+		LocalRouter *sacloud.LocalRouter
+		Error       error
+	}{
+		LocalRouter: resultLocalRouter,
+		Error:       err,
+	}
+	if d, err := json.Marshal(targetResults); err == nil {
+		log.Printf("[TRACE] \tresults: %s\n", string(d))
+	}
+
+	return resultLocalRouter, err
+}
+
+// Delete is API call with trace log
+func (t *LocalRouterTracer) Delete(ctx context.Context, id types.ID) error {
+	log.Println("[TRACE] LocalRouterAPI.Delete start")
+	targetArguments := struct {
+		Argid types.ID `json:"id"`
+	}{
+		Argid: id,
+	}
+	if d, err := json.Marshal(targetArguments); err == nil {
+		log.Printf("[TRACE] \targs: %s\n", string(d))
+	}
+
+	defer func() {
+		log.Println("[TRACE] LocalRouterAPI.Delete end")
+	}()
+
+	err := t.Internal.Delete(ctx, id)
+	targetResults := struct {
+		Error error
+	}{
+		Error: err,
+	}
+	if d, err := json.Marshal(targetResults); err == nil {
+		log.Printf("[TRACE] \tresults: %s\n", string(d))
+	}
+
+	return err
+}
+
+// HealthStatus is API call with trace log
+func (t *LocalRouterTracer) HealthStatus(ctx context.Context, id types.ID) (*sacloud.LocalRouterHealth, error) {
+	log.Println("[TRACE] LocalRouterAPI.HealthStatus start")
+	targetArguments := struct {
+		Argid types.ID `json:"id"`
+	}{
+		Argid: id,
+	}
+	if d, err := json.Marshal(targetArguments); err == nil {
+		log.Printf("[TRACE] \targs: %s\n", string(d))
+	}
+
+	defer func() {
+		log.Println("[TRACE] LocalRouterAPI.HealthStatus end")
+	}()
+
+	resultLocalRouterHealth, err := t.Internal.HealthStatus(ctx, id)
+	targetResults := struct {
+		LocalRouterHealth *sacloud.LocalRouterHealth
+		Error             error
+	}{
+		LocalRouterHealth: resultLocalRouterHealth,
+		Error:             err,
+	}
+	if d, err := json.Marshal(targetResults); err == nil {
+		log.Printf("[TRACE] \tresults: %s\n", string(d))
+	}
+
+	return resultLocalRouterHealth, err
+}
+
+// MonitorLocalRouter is API call with trace log
+func (t *LocalRouterTracer) MonitorLocalRouter(ctx context.Context, id types.ID, condition *sacloud.MonitorCondition) (*sacloud.LocalRouterActivity, error) {
+	log.Println("[TRACE] LocalRouterAPI.MonitorLocalRouter start")
+	targetArguments := struct {
+		Argid        types.ID                  `json:"id"`
+		Argcondition *sacloud.MonitorCondition `json:"condition"`
+	}{
+		Argid:        id,
+		Argcondition: condition,
+	}
+	if d, err := json.Marshal(targetArguments); err == nil {
+		log.Printf("[TRACE] \targs: %s\n", string(d))
+	}
+
+	defer func() {
+		log.Println("[TRACE] LocalRouterAPI.MonitorLocalRouter end")
+	}()
+
+	resultLocalRouterActivity, err := t.Internal.MonitorLocalRouter(ctx, id, condition)
+	targetResults := struct {
+		LocalRouterActivity *sacloud.LocalRouterActivity
+		Error               error
+	}{
+		LocalRouterActivity: resultLocalRouterActivity,
+		Error:               err,
+	}
+	if d, err := json.Marshal(targetResults); err == nil {
+		log.Printf("[TRACE] \tresults: %s\n", string(d))
+	}
+
+	return resultLocalRouterActivity, err
+}
+
 /*************************************************
 * MobileGatewayTracer
 *************************************************/
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_api_ops.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_api_ops.go
index 98895cd4..3f842e18 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_api_ops.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_api_ops.go
@@ -211,6 +211,14 @@ func init() {
 		}
 	})
 
+	SetClientFactoryFunc("LocalRouter", func(caller APICaller) interface{} {
+		return &LocalRouterOp{
+			Client:     caller,
+			PathSuffix: "api/cloud/1.1",
+			PathName:   "commonserviceitem",
+		}
+	})
+
 	SetClientFactoryFunc("MobileGateway", func(caller APICaller) interface{} {
 		return &MobileGatewayOp{
 			Client:     caller,
@@ -5799,6 +5807,306 @@ func (o *LoadBalancerOp) Status(ctx context.Context, zone string, id types.ID) (
 	return results, err
 }
 
+/*************************************************
+* LocalRouterOp
+*************************************************/
+
+// LocalRouterOp implements LocalRouterAPI interface
+type LocalRouterOp struct {
+	// Client APICaller
+	Client APICaller
+	// PathSuffix is used when building URL
+	PathSuffix string
+	// PathName is used when building URL
+	PathName string
+}
+
+// NewLocalRouterOp creates new LocalRouterOp instance
+func NewLocalRouterOp(caller APICaller) LocalRouterAPI {
+	return GetClientFactoryFunc("LocalRouter")(caller).(LocalRouterAPI)
+}
+
+// Find is API call
+func (o *LocalRouterOp) Find(ctx context.Context, conditions *FindCondition) (*LocalRouterFindResult, error) {
+	// build request URL
+	pathBuildParameter := map[string]interface{}{
+		"rootURL":    SakuraCloudAPIRoot,
+		"pathSuffix": o.PathSuffix,
+		"pathName":   o.PathName,
+		"zone":       APIDefaultZone,
+		"conditions": conditions,
+	}
+
+	url, err := buildURL("{{.rootURL}}/{{.zone}}/{{.pathSuffix}}/{{.pathName}}", pathBuildParameter)
+	if err != nil {
+		return nil, err
+	}
+	// build request body
+	var body interface{}
+	v, err := o.transformFindArgs(conditions)
+	if err != nil {
+		return nil, err
+	}
+	body = v
+
+	// do request
+	data, err := o.Client.Do(ctx, "GET", url, body)
+	if err != nil {
+		return nil, err
+	}
+
+	// build results
+	results, err := o.transformFindResults(data)
+	if err != nil {
+		return nil, err
+	}
+	return results, err
+}
+
+// Create is API call
+func (o *LocalRouterOp) Create(ctx context.Context, param *LocalRouterCreateRequest) (*LocalRouter, error) {
+	// build request URL
+	pathBuildParameter := map[string]interface{}{
+		"rootURL":    SakuraCloudAPIRoot,
+		"pathSuffix": o.PathSuffix,
+		"pathName":   o.PathName,
+		"zone":       APIDefaultZone,
+		"param":      param,
+	}
+
+	url, err := buildURL("{{.rootURL}}/{{.zone}}/{{.pathSuffix}}/{{.pathName}}", pathBuildParameter)
+	if err != nil {
+		return nil, err
+	}
+	// build request body
+	var body interface{}
+	v, err := o.transformCreateArgs(param)
+	if err != nil {
+		return nil, err
+	}
+	body = v
+
+	// do request
+	data, err := o.Client.Do(ctx, "POST", url, body)
+	if err != nil {
+		return nil, err
+	}
+
+	// build results
+	results, err := o.transformCreateResults(data)
+	if err != nil {
+		return nil, err
+	}
+	return results.LocalRouter, nil
+}
+
+// Read is API call
+func (o *LocalRouterOp) Read(ctx context.Context, id types.ID) (*LocalRouter, error) {
+	// build request URL
+	pathBuildParameter := map[string]interface{}{
+		"rootURL":    SakuraCloudAPIRoot,
+		"pathSuffix": o.PathSuffix,
+		"pathName":   o.PathName,
+		"zone":       APIDefaultZone,
+		"id":         id,
+	}
+
+	url, err := buildURL("{{.rootURL}}/{{.zone}}/{{.pathSuffix}}/{{.pathName}}/{{.id}}", pathBuildParameter)
+	if err != nil {
+		return nil, err
+	}
+	// build request body
+	var body interface{}
+
+	// do request
+	data, err := o.Client.Do(ctx, "GET", url, body)
+	if err != nil {
+		return nil, err
+	}
+
+	// build results
+	results, err := o.transformReadResults(data)
+	if err != nil {
+		return nil, err
+	}
+	return results.LocalRouter, nil
+}
+
+// Update is API call
+func (o *LocalRouterOp) Update(ctx context.Context, id types.ID, param *LocalRouterUpdateRequest) (*LocalRouter, error) {
+	// build request URL
+	pathBuildParameter := map[string]interface{}{
+		"rootURL":    SakuraCloudAPIRoot,
+		"pathSuffix": o.PathSuffix,
+		"pathName":   o.PathName,
+		"zone":       APIDefaultZone,
+		"id":         id,
+		"param":      param,
+	}
+
+	url, err := buildURL("{{.rootURL}}/{{.zone}}/{{.pathSuffix}}/{{.pathName}}/{{.id}}", pathBuildParameter)
+	if err != nil {
+		return nil, err
+	}
+	// build request body
+	var body interface{}
+	v, err := o.transformUpdateArgs(id, param)
+	if err != nil {
+		return nil, err
+	}
+	body = v
+
+	// do request
+	data, err := o.Client.Do(ctx, "PUT", url, body)
+	if err != nil {
+		return nil, err
+	}
+
+	// build results
+	results, err := o.transformUpdateResults(data)
+	if err != nil {
+		return nil, err
+	}
+	return results.LocalRouter, nil
+}
+
+// UpdateSettings is API call
+func (o *LocalRouterOp) UpdateSettings(ctx context.Context, id types.ID, param *LocalRouterUpdateSettingsRequest) (*LocalRouter, error) {
+	// build request URL
+	pathBuildParameter := map[string]interface{}{
+		"rootURL":    SakuraCloudAPIRoot,
+		"pathSuffix": o.PathSuffix,
+		"pathName":   o.PathName,
+		"zone":       APIDefaultZone,
+		"id":         id,
+		"param":      param,
+	}
+
+	url, err := buildURL("{{.rootURL}}/{{.zone}}/{{.pathSuffix}}/{{.pathName}}/{{.id}}", pathBuildParameter)
+	if err != nil {
+		return nil, err
+	}
+	// build request body
+	var body interface{}
+	v, err := o.transformUpdateSettingsArgs(id, param)
+	if err != nil {
+		return nil, err
+	}
+	body = v
+
+	// do request
+	data, err := o.Client.Do(ctx, "PUT", url, body)
+	if err != nil {
+		return nil, err
+	}
+
+	// build results
+	results, err := o.transformUpdateSettingsResults(data)
+	if err != nil {
+		return nil, err
+	}
+	return results.LocalRouter, nil
+}
+
+// Delete is API call
+func (o *LocalRouterOp) Delete(ctx context.Context, id types.ID) error {
+	// build request URL
+	pathBuildParameter := map[string]interface{}{
+		"rootURL":    SakuraCloudAPIRoot,
+		"pathSuffix": o.PathSuffix,
+		"pathName":   o.PathName,
+		"zone":       APIDefaultZone,
+		"id":         id,
+	}
+
+	url, err := buildURL("{{.rootURL}}/{{.zone}}/{{.pathSuffix}}/{{.pathName}}/{{.id}}", pathBuildParameter)
+	if err != nil {
+		return err
+	}
+	// build request body
+	var body interface{}
+
+	// do request
+	_, err = o.Client.Do(ctx, "DELETE", url, body)
+	if err != nil {
+		return err
+	}
+
+	// build results
+
+	return nil
+}
+
+// HealthStatus is API call
+func (o *LocalRouterOp) HealthStatus(ctx context.Context, id types.ID) (*LocalRouterHealth, error) {
+	// build request URL
+	pathBuildParameter := map[string]interface{}{
+		"rootURL":    SakuraCloudAPIRoot,
+		"pathSuffix": o.PathSuffix,
+		"pathName":   o.PathName,
+		"zone":       APIDefaultZone,
+		"id":         id,
+	}
+
+	url, err := buildURL("{{.rootURL}}/{{.zone}}/{{.pathSuffix}}/{{.pathName}}/{{.id}}/health", pathBuildParameter)
+	if err != nil {
+		return nil, err
+	}
+	// build request body
+	var body interface{}
+
+	// do request
+	data, err := o.Client.Do(ctx, "GET", url, body)
+	if err != nil {
+		return nil, err
+	}
+
+	// build results
+	results, err := o.transformHealthStatusResults(data)
+	if err != nil {
+		return nil, err
+	}
+	return results.LocalRouterHealth, nil
+}
+
+// MonitorLocalRouter is API call
+func (o *LocalRouterOp) MonitorLocalRouter(ctx context.Context, id types.ID, condition *MonitorCondition) (*LocalRouterActivity, error) {
+	// build request URL
+	pathBuildParameter := map[string]interface{}{
+		"rootURL":    SakuraCloudAPIRoot,
+		"pathSuffix": o.PathSuffix,
+		"pathName":   o.PathName,
+		"zone":       APIDefaultZone,
+		"id":         id,
+		"condition":  condition,
+	}
+
+	url, err := buildURL("{{.rootURL}}/{{.zone}}/{{.pathSuffix}}/{{.pathName}}/{{.id}}/activity/localrouter/monitor", pathBuildParameter)
+	if err != nil {
+		return nil, err
+	}
+	// build request body
+	var body interface{}
+	v, err := o.transformMonitorLocalRouterArgs(id, condition)
+	if err != nil {
+		return nil, err
+	}
+	body = v
+
+	// do request
+	data, err := o.Client.Do(ctx, "GET", url, body)
+	if err != nil {
+		return nil, err
+	}
+
+	// build results
+	results, err := o.transformMonitorLocalRouterResults(data)
+	if err != nil {
+		return nil, err
+	}
+	return results.LocalRouterActivity, nil
+}
+
 /*************************************************
 * MobileGatewayOp
 *************************************************/
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_api_transformers.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_api_transformers.go
index 6248c3cf..1a9551d0 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_api_transformers.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_api_transformers.go
@@ -3446,6 +3446,229 @@ func (o *LoadBalancerOp) transformStatusResults(data []byte) (*LoadBalancerStatu
 	return results, nil
 }
 
+func (o *LocalRouterOp) transformFindArgs(conditions *FindCondition) (*localRouterFindRequestEnvelope, error) {
+	if conditions == nil {
+		conditions = &FindCondition{}
+	}
+	var arg0 interface{} = conditions
+	if v, ok := arg0.(argumentDefaulter); ok {
+		arg0 = v.setDefaults()
+	}
+	args := &struct {
+		Arg0 interface{} `mapconv:",squash"`
+	}{
+		Arg0: arg0,
+	}
+
+	v := &localRouterFindRequestEnvelope{}
+	if err := mapconv.ConvertTo(args, v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+func (o *LocalRouterOp) transformFindResults(data []byte) (*LocalRouterFindResult, error) {
+	nakedResponse := &localRouterFindResponseEnvelope{}
+	if err := json.Unmarshal(data, nakedResponse); err != nil {
+		return nil, err
+	}
+
+	results := &LocalRouterFindResult{}
+	if err := mapconv.ConvertFrom(nakedResponse, results); err != nil {
+		return nil, err
+	}
+	return results, nil
+}
+
+func (o *LocalRouterOp) transformCreateArgs(param *LocalRouterCreateRequest) (*localRouterCreateRequestEnvelope, error) {
+	if param == nil {
+		param = &LocalRouterCreateRequest{}
+	}
+	var arg0 interface{} = param
+	if v, ok := arg0.(argumentDefaulter); ok {
+		arg0 = v.setDefaults()
+	}
+	args := &struct {
+		Arg0 interface{} `mapconv:"CommonServiceItem,recursive"`
+	}{
+		Arg0: arg0,
+	}
+
+	v := &localRouterCreateRequestEnvelope{}
+	if err := mapconv.ConvertTo(args, v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+func (o *LocalRouterOp) transformCreateResults(data []byte) (*localRouterCreateResult, error) {
+	nakedResponse := &localRouterCreateResponseEnvelope{}
+	if err := json.Unmarshal(data, nakedResponse); err != nil {
+		return nil, err
+	}
+
+	results := &localRouterCreateResult{}
+	if err := mapconv.ConvertFrom(nakedResponse, results); err != nil {
+		return nil, err
+	}
+	return results, nil
+}
+
+func (o *LocalRouterOp) transformReadResults(data []byte) (*localRouterReadResult, error) {
+	nakedResponse := &localRouterReadResponseEnvelope{}
+	if err := json.Unmarshal(data, nakedResponse); err != nil {
+		return nil, err
+	}
+
+	results := &localRouterReadResult{}
+	if err := mapconv.ConvertFrom(nakedResponse, results); err != nil {
+		return nil, err
+	}
+	return results, nil
+}
+
+func (o *LocalRouterOp) transformUpdateArgs(id types.ID, param *LocalRouterUpdateRequest) (*localRouterUpdateRequestEnvelope, error) {
+	if id == types.ID(int64(0)) {
+		id = types.ID(int64(0))
+	}
+	var arg0 interface{} = id
+	if v, ok := arg0.(argumentDefaulter); ok {
+		arg0 = v.setDefaults()
+	}
+	if param == nil {
+		param = &LocalRouterUpdateRequest{}
+	}
+	var arg1 interface{} = param
+	if v, ok := arg1.(argumentDefaulter); ok {
+		arg1 = v.setDefaults()
+	}
+	args := &struct {
+		Arg0 interface{}
+		Arg1 interface{} `mapconv:"CommonServiceItem,recursive"`
+	}{
+		Arg0: arg0,
+		Arg1: arg1,
+	}
+
+	v := &localRouterUpdateRequestEnvelope{}
+	if err := mapconv.ConvertTo(args, v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+func (o *LocalRouterOp) transformUpdateResults(data []byte) (*localRouterUpdateResult, error) {
+	nakedResponse := &localRouterUpdateResponseEnvelope{}
+	if err := json.Unmarshal(data, nakedResponse); err != nil {
+		return nil, err
+	}
+
+	results := &localRouterUpdateResult{}
+	if err := mapconv.ConvertFrom(nakedResponse, results); err != nil {
+		return nil, err
+	}
+	return results, nil
+}
+
+func (o *LocalRouterOp) transformUpdateSettingsArgs(id types.ID, param *LocalRouterUpdateSettingsRequest) (*localRouterUpdateSettingsRequestEnvelope, error) {
+	if id == types.ID(int64(0)) {
+		id = types.ID(int64(0))
+	}
+	var arg0 interface{} = id
+	if v, ok := arg0.(argumentDefaulter); ok {
+		arg0 = v.setDefaults()
+	}
+	if param == nil {
+		param = &LocalRouterUpdateSettingsRequest{}
+	}
+	var arg1 interface{} = param
+	if v, ok := arg1.(argumentDefaulter); ok {
+		arg1 = v.setDefaults()
+	}
+	args := &struct {
+		Arg0 interface{}
+		Arg1 interface{} `mapconv:"CommonServiceItem,recursive"`
+	}{
+		Arg0: arg0,
+		Arg1: arg1,
+	}
+
+	v := &localRouterUpdateSettingsRequestEnvelope{}
+	if err := mapconv.ConvertTo(args, v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+func (o *LocalRouterOp) transformUpdateSettingsResults(data []byte) (*localRouterUpdateSettingsResult, error) {
+	nakedResponse := &localRouterUpdateSettingsResponseEnvelope{}
+	if err := json.Unmarshal(data, nakedResponse); err != nil {
+		return nil, err
+	}
+
+	results := &localRouterUpdateSettingsResult{}
+	if err := mapconv.ConvertFrom(nakedResponse, results); err != nil {
+		return nil, err
+	}
+	return results, nil
+}
+
+func (o *LocalRouterOp) transformHealthStatusResults(data []byte) (*localRouterHealthStatusResult, error) {
+	nakedResponse := &localRouterHealthStatusResponseEnvelope{}
+	if err := json.Unmarshal(data, nakedResponse); err != nil {
+		return nil, err
+	}
+
+	results := &localRouterHealthStatusResult{}
+	if err := mapconv.ConvertFrom(nakedResponse, results); err != nil {
+		return nil, err
+	}
+	return results, nil
+}
+
+func (o *LocalRouterOp) transformMonitorLocalRouterArgs(id types.ID, condition *MonitorCondition) (*localRouterMonitorLocalRouterRequestEnvelope, error) {
+	if id == types.ID(int64(0)) {
+		id = types.ID(int64(0))
+	}
+	var arg0 interface{} = id
+	if v, ok := arg0.(argumentDefaulter); ok {
+		arg0 = v.setDefaults()
+	}
+	if condition == nil {
+		condition = &MonitorCondition{}
+	}
+	var arg1 interface{} = condition
+	if v, ok := arg1.(argumentDefaulter); ok {
+		arg1 = v.setDefaults()
+	}
+	args := &struct {
+		Arg0 interface{}
+		Arg1 interface{} `mapconv:",squash"`
+	}{
+		Arg0: arg0,
+		Arg1: arg1,
+	}
+
+	v := &localRouterMonitorLocalRouterRequestEnvelope{}
+	if err := mapconv.ConvertTo(args, v); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
+
+func (o *LocalRouterOp) transformMonitorLocalRouterResults(data []byte) (*localRouterMonitorLocalRouterResult, error) {
+	nakedResponse := &localRouterMonitorLocalRouterResponseEnvelope{}
+	if err := json.Unmarshal(data, nakedResponse); err != nil {
+		return nil, err
+	}
+
+	results := &localRouterMonitorLocalRouterResult{}
+	if err := mapconv.ConvertFrom(nakedResponse, results); err != nil {
+		return nil, err
+	}
+	return results, nil
+}
+
 func (o *MobileGatewayOp) transformFindArgs(conditions *FindCondition) (*mobileGatewayFindRequestEnvelope, error) {
 	if conditions == nil {
 		conditions = &FindCondition{}
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_apis.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_apis.go
index ce61830a..86ef094a 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_apis.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_apis.go
@@ -351,6 +351,22 @@ type LoadBalancerAPI interface {
 	Status(ctx context.Context, zone string, id types.ID) (*LoadBalancerStatusResult, error)
 }
 
+/*************************************************
+* LocalRouterAPI
+*************************************************/
+
+// LocalRouterAPI is interface for operate LocalRouter resource
+type LocalRouterAPI interface {
+	Find(ctx context.Context, conditions *FindCondition) (*LocalRouterFindResult, error)
+	Create(ctx context.Context, param *LocalRouterCreateRequest) (*LocalRouter, error)
+	Read(ctx context.Context, id types.ID) (*LocalRouter, error)
+	Update(ctx context.Context, id types.ID, param *LocalRouterUpdateRequest) (*LocalRouter, error)
+	UpdateSettings(ctx context.Context, id types.ID, param *LocalRouterUpdateSettingsRequest) (*LocalRouter, error)
+	Delete(ctx context.Context, id types.ID) error
+	HealthStatus(ctx context.Context, id types.ID) (*LocalRouterHealth, error)
+	MonitorLocalRouter(ctx context.Context, id types.ID, condition *MonitorCondition) (*LocalRouterActivity, error)
+}
+
 /*************************************************
 * MobileGatewayAPI
 *************************************************/
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_envelopes.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_envelopes.go
index 8ed9ba31..af6f5c76 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_envelopes.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_envelopes.go
@@ -1392,6 +1392,94 @@ type loadBalancerStatusResponseEnvelope struct {
 	LoadBalancer []*naked.LoadBalancerStatus `json:",omitempty"`
 }
 
+// localRouterFindRequestEnvelope is envelop of API request
+type localRouterFindRequestEnvelope struct {
+	Count   int             `mapconv:",omitempty"`
+	From    int             `mapconv:",omitempty"`
+	Sort    search.SortKeys `json:",omitempty" mapconv:",omitempty"`
+	Filter  search.Filter   `json:",omitempty" mapconv:",omitempty"`
+	Include []string        `json:",omitempty" mapconv:",omitempty"`
+	Exclude []string        `json:",omitempty" mapconv:",omitempty"`
+}
+
+// localRouterFindResponseEnvelope is envelop of API response
+type localRouterFindResponseEnvelope struct {
+	Total int `json:",omitempty"` // トータル件数
+	From  int `json:",omitempty"` // ページング開始ページ
+	Count int `json:",omitempty"` // 件数
+
+	CommonServiceItems []*naked.LocalRouter `json:",omitempty"`
+}
+
+// localRouterCreateRequestEnvelope is envelop of API request
+type localRouterCreateRequestEnvelope struct {
+	CommonServiceItem *naked.LocalRouter `json:",omitempty"`
+}
+
+// localRouterCreateResponseEnvelope is envelop of API response
+type localRouterCreateResponseEnvelope struct {
+	IsOk    bool            `json:"is_ok,omitempty"` // is_ok項目
+	Success types.APIResult `json:",omitempty"`      // success項目
+
+	CommonServiceItem *naked.LocalRouter `json:",omitempty"`
+}
+
+// localRouterReadResponseEnvelope is envelop of API response
+type localRouterReadResponseEnvelope struct {
+	IsOk    bool            `json:"is_ok,omitempty"` // is_ok項目
+	Success types.APIResult `json:",omitempty"`      // success項目
+
+	CommonServiceItem *naked.LocalRouter `json:",omitempty"`
+}
+
+// localRouterUpdateRequestEnvelope is envelop of API request
+type localRouterUpdateRequestEnvelope struct {
+	CommonServiceItem *naked.LocalRouter `json:",omitempty"`
+}
+
+// localRouterUpdateResponseEnvelope is envelop of API response
+type localRouterUpdateResponseEnvelope struct {
+	IsOk    bool            `json:"is_ok,omitempty"` // is_ok項目
+	Success types.APIResult `json:",omitempty"`      // success項目
+
+	CommonServiceItem *naked.LocalRouter `json:",omitempty"`
+}
+
+// localRouterUpdateSettingsRequestEnvelope is envelop of API request
+type localRouterUpdateSettingsRequestEnvelope struct {
+	CommonServiceItem *naked.LocalRouterSettingsUpdate `json:",omitempty"`
+}
+
+// localRouterUpdateSettingsResponseEnvelope is envelop of API response
+type localRouterUpdateSettingsResponseEnvelope struct {
+	IsOk    bool            `json:"is_ok,omitempty"` // is_ok項目
+	Success types.APIResult `json:",omitempty"`      // success項目
+
+	CommonServiceItem *naked.LocalRouter `json:",omitempty"`
+}
+
+// localRouterHealthStatusResponseEnvelope is envelop of API response
+type localRouterHealthStatusResponseEnvelope struct {
+	IsOk    bool            `json:"is_ok,omitempty"` // is_ok項目
+	Success types.APIResult `json:",omitempty"`      // success項目
+
+	LocalRouter *naked.LocalRouterHealth `json:",omitempty"`
+}
+
+// localRouterMonitorLocalRouterRequestEnvelope is envelop of API request
+type localRouterMonitorLocalRouterRequestEnvelope struct {
+	Start time.Time `json:",omitempty"`
+	End   time.Time `json:",omitempty"`
+}
+
+// localRouterMonitorLocalRouterResponseEnvelope is envelop of API response
+type localRouterMonitorLocalRouterResponseEnvelope struct {
+	IsOk    bool            `json:"is_ok,omitempty"` // is_ok項目
+	Success types.APIResult `json:",omitempty"`      // success項目
+
+	Data *naked.MonitorValues `json:",omitempty"`
+}
+
 // mobileGatewayFindRequestEnvelope is envelop of API request
 type mobileGatewayFindRequestEnvelope struct {
 	Count   int             `mapconv:",omitempty"`
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_models.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_models.go
index cc2412e4..6b18672f 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_models.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_models.go
@@ -12550,6 +12550,1017 @@ func (o *LoadBalancerServerStatus) SetCPS(v types.StringNumber) {
 	o.CPS = v
 }
 
+/*************************************************
+* LocalRouter
+*************************************************/
+
+// LocalRouter represents API parameter/response structure
+type LocalRouter struct {
+	ID           types.ID
+	Name         string `validate:"required"`
+	Description  string `validate:"min=0,max=512"`
+	Tags         types.Tags
+	Availability types.EAvailability
+	IconID       types.ID `mapconv:"Icon.ID"`
+	CreatedAt    time.Time
+	ModifiedAt   time.Time
+	Switch       *LocalRouterSwitch        `mapconv:"Settings.LocalRouter.Switch,recursive"`
+	Interface    *LocalRouterInterface     `mapconv:"Settings.LocalRouter.Interface,recursive"`
+	Peers        []*LocalRouterPeer        `mapconv:"Settings.LocalRouter.[]Peers,recursive"`
+	StaticRoutes []*LocalRouterStaticRoute `mapconv:"Settings.LocalRouter.[]StaticRoutes,recursive"`
+	SettingsHash string                    `json:",omitempty" mapconv:",omitempty"`
+	SecretKeys   []string                  `mapconv:"Status.SecretKeys"`
+}
+
+// Validate validates by field tags
+func (o *LocalRouter) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouter) setDefaults() interface{} {
+	return &struct {
+		ID           types.ID
+		Name         string `validate:"required"`
+		Description  string `validate:"min=0,max=512"`
+		Tags         types.Tags
+		Availability types.EAvailability
+		IconID       types.ID `mapconv:"Icon.ID"`
+		CreatedAt    time.Time
+		ModifiedAt   time.Time
+		Switch       *LocalRouterSwitch        `mapconv:"Settings.LocalRouter.Switch,recursive"`
+		Interface    *LocalRouterInterface     `mapconv:"Settings.LocalRouter.Interface,recursive"`
+		Peers        []*LocalRouterPeer        `mapconv:"Settings.LocalRouter.[]Peers,recursive"`
+		StaticRoutes []*LocalRouterStaticRoute `mapconv:"Settings.LocalRouter.[]StaticRoutes,recursive"`
+		SettingsHash string                    `json:",omitempty" mapconv:",omitempty"`
+		SecretKeys   []string                  `mapconv:"Status.SecretKeys"`
+	}{
+		ID:           o.GetID(),
+		Name:         o.GetName(),
+		Description:  o.GetDescription(),
+		Tags:         o.GetTags(),
+		Availability: o.GetAvailability(),
+		IconID:       o.GetIconID(),
+		CreatedAt:    o.GetCreatedAt(),
+		ModifiedAt:   o.GetModifiedAt(),
+		Switch:       o.GetSwitch(),
+		Interface:    o.GetInterface(),
+		Peers:        o.GetPeers(),
+		StaticRoutes: o.GetStaticRoutes(),
+		SettingsHash: o.GetSettingsHash(),
+		SecretKeys:   o.GetSecretKeys(),
+	}
+}
+
+// GetID returns value of ID
+func (o *LocalRouter) GetID() types.ID {
+	return o.ID
+}
+
+// SetID sets value to ID
+func (o *LocalRouter) SetID(v types.ID) {
+	o.ID = v
+}
+
+// SetStringID .
+func (o *LocalRouter) SetStringID(id string) {
+	accessor.SetStringID(o, id)
+}
+
+// GetStringID .
+func (o *LocalRouter) GetStringID() string {
+	return accessor.GetStringID(o)
+}
+
+// SetInt64ID .
+func (o *LocalRouter) SetInt64ID(id int64) {
+	accessor.SetInt64ID(o, id)
+}
+
+// GetInt64ID .
+func (o *LocalRouter) GetInt64ID() int64 {
+	return accessor.GetInt64ID(o)
+}
+
+// GetName returns value of Name
+func (o *LocalRouter) GetName() string {
+	return o.Name
+}
+
+// SetName sets value to Name
+func (o *LocalRouter) SetName(v string) {
+	o.Name = v
+}
+
+// GetDescription returns value of Description
+func (o *LocalRouter) GetDescription() string {
+	return o.Description
+}
+
+// SetDescription sets value to Description
+func (o *LocalRouter) SetDescription(v string) {
+	o.Description = v
+}
+
+// GetTags returns value of Tags
+func (o *LocalRouter) GetTags() types.Tags {
+	return o.Tags
+}
+
+// SetTags sets value to Tags
+func (o *LocalRouter) SetTags(v types.Tags) {
+	o.Tags = v
+}
+
+// HasTag 指定のタグが存在する場合trueを返す
+func (o *LocalRouter) HasTag(tag string) bool {
+	return accessor.HasTag(o, tag)
+}
+
+// AppendTag 指定のタグを追加
+func (o *LocalRouter) AppendTag(tag string) {
+	accessor.AppendTag(o, tag)
+}
+
+// RemoveTag 指定のタグを削除
+func (o *LocalRouter) RemoveTag(tag string) {
+	accessor.RemoveTag(o, tag)
+}
+
+// ClearTags タグを全クリア
+func (o *LocalRouter) ClearTags() {
+	accessor.ClearTags(o)
+}
+
+// GetAvailability returns value of Availability
+func (o *LocalRouter) GetAvailability() types.EAvailability {
+	return o.Availability
+}
+
+// SetAvailability sets value to Availability
+func (o *LocalRouter) SetAvailability(v types.EAvailability) {
+	o.Availability = v
+}
+
+// GetIconID returns value of IconID
+func (o *LocalRouter) GetIconID() types.ID {
+	return o.IconID
+}
+
+// SetIconID sets value to IconID
+func (o *LocalRouter) SetIconID(v types.ID) {
+	o.IconID = v
+}
+
+// GetCreatedAt returns value of CreatedAt
+func (o *LocalRouter) GetCreatedAt() time.Time {
+	return o.CreatedAt
+}
+
+// SetCreatedAt sets value to CreatedAt
+func (o *LocalRouter) SetCreatedAt(v time.Time) {
+	o.CreatedAt = v
+}
+
+// GetModifiedAt returns value of ModifiedAt
+func (o *LocalRouter) GetModifiedAt() time.Time {
+	return o.ModifiedAt
+}
+
+// SetModifiedAt sets value to ModifiedAt
+func (o *LocalRouter) SetModifiedAt(v time.Time) {
+	o.ModifiedAt = v
+}
+
+// GetSwitch returns value of Switch
+func (o *LocalRouter) GetSwitch() *LocalRouterSwitch {
+	return o.Switch
+}
+
+// SetSwitch sets value to Switch
+func (o *LocalRouter) SetSwitch(v *LocalRouterSwitch) {
+	o.Switch = v
+}
+
+// GetInterface returns value of Interface
+func (o *LocalRouter) GetInterface() *LocalRouterInterface {
+	return o.Interface
+}
+
+// SetInterface sets value to Interface
+func (o *LocalRouter) SetInterface(v *LocalRouterInterface) {
+	o.Interface = v
+}
+
+// GetPeers returns value of Peers
+func (o *LocalRouter) GetPeers() []*LocalRouterPeer {
+	return o.Peers
+}
+
+// SetPeers sets value to Peers
+func (o *LocalRouter) SetPeers(v []*LocalRouterPeer) {
+	o.Peers = v
+}
+
+// GetStaticRoutes returns value of StaticRoutes
+func (o *LocalRouter) GetStaticRoutes() []*LocalRouterStaticRoute {
+	return o.StaticRoutes
+}
+
+// SetStaticRoutes sets value to StaticRoutes
+func (o *LocalRouter) SetStaticRoutes(v []*LocalRouterStaticRoute) {
+	o.StaticRoutes = v
+}
+
+// GetSettingsHash returns value of SettingsHash
+func (o *LocalRouter) GetSettingsHash() string {
+	return o.SettingsHash
+}
+
+// SetSettingsHash sets value to SettingsHash
+func (o *LocalRouter) SetSettingsHash(v string) {
+	o.SettingsHash = v
+}
+
+// GetSecretKeys returns value of SecretKeys
+func (o *LocalRouter) GetSecretKeys() []string {
+	return o.SecretKeys
+}
+
+// SetSecretKeys sets value to SecretKeys
+func (o *LocalRouter) SetSecretKeys(v []string) {
+	o.SecretKeys = v
+}
+
+/*************************************************
+* LocalRouterSwitch
+*************************************************/
+
+// LocalRouterSwitch represents API parameter/response structure
+type LocalRouterSwitch struct {
+	Code     string
+	Category string
+	ZoneID   string
+}
+
+// Validate validates by field tags
+func (o *LocalRouterSwitch) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterSwitch) setDefaults() interface{} {
+	return &struct {
+		Code     string
+		Category string
+		ZoneID   string
+	}{
+		Code:     o.GetCode(),
+		Category: o.GetCategory(),
+		ZoneID:   o.GetZoneID(),
+	}
+}
+
+// GetCode returns value of Code
+func (o *LocalRouterSwitch) GetCode() string {
+	return o.Code
+}
+
+// SetCode sets value to Code
+func (o *LocalRouterSwitch) SetCode(v string) {
+	o.Code = v
+}
+
+// GetCategory returns value of Category
+func (o *LocalRouterSwitch) GetCategory() string {
+	return o.Category
+}
+
+// SetCategory sets value to Category
+func (o *LocalRouterSwitch) SetCategory(v string) {
+	o.Category = v
+}
+
+// GetZoneID returns value of ZoneID
+func (o *LocalRouterSwitch) GetZoneID() string {
+	return o.ZoneID
+}
+
+// SetZoneID sets value to ZoneID
+func (o *LocalRouterSwitch) SetZoneID(v string) {
+	o.ZoneID = v
+}
+
+/*************************************************
+* LocalRouterInterface
+*************************************************/
+
+// LocalRouterInterface represents API parameter/response structure
+type LocalRouterInterface struct {
+	VirtualIPAddress string
+	IPAddress        []string
+	NetworkMaskLen   int
+	VRID             int
+}
+
+// Validate validates by field tags
+func (o *LocalRouterInterface) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterInterface) setDefaults() interface{} {
+	return &struct {
+		VirtualIPAddress string
+		IPAddress        []string
+		NetworkMaskLen   int
+		VRID             int
+	}{
+		VirtualIPAddress: o.GetVirtualIPAddress(),
+		IPAddress:        o.GetIPAddress(),
+		NetworkMaskLen:   o.GetNetworkMaskLen(),
+		VRID:             o.GetVRID(),
+	}
+}
+
+// GetVirtualIPAddress returns value of VirtualIPAddress
+func (o *LocalRouterInterface) GetVirtualIPAddress() string {
+	return o.VirtualIPAddress
+}
+
+// SetVirtualIPAddress sets value to VirtualIPAddress
+func (o *LocalRouterInterface) SetVirtualIPAddress(v string) {
+	o.VirtualIPAddress = v
+}
+
+// GetIPAddress returns value of IPAddress
+func (o *LocalRouterInterface) GetIPAddress() []string {
+	return o.IPAddress
+}
+
+// SetIPAddress sets value to IPAddress
+func (o *LocalRouterInterface) SetIPAddress(v []string) {
+	o.IPAddress = v
+}
+
+// GetNetworkMaskLen returns value of NetworkMaskLen
+func (o *LocalRouterInterface) GetNetworkMaskLen() int {
+	return o.NetworkMaskLen
+}
+
+// SetNetworkMaskLen sets value to NetworkMaskLen
+func (o *LocalRouterInterface) SetNetworkMaskLen(v int) {
+	o.NetworkMaskLen = v
+}
+
+// GetVRID returns value of VRID
+func (o *LocalRouterInterface) GetVRID() int {
+	return o.VRID
+}
+
+// SetVRID sets value to VRID
+func (o *LocalRouterInterface) SetVRID(v int) {
+	o.VRID = v
+}
+
+/*************************************************
+* LocalRouterPeer
+*************************************************/
+
+// LocalRouterPeer represents API parameter/response structure
+type LocalRouterPeer struct {
+	ID          types.ID
+	SecretKey   string
+	Enabled     bool
+	Description string
+}
+
+// Validate validates by field tags
+func (o *LocalRouterPeer) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterPeer) setDefaults() interface{} {
+	return &struct {
+		ID          types.ID
+		SecretKey   string
+		Enabled     bool
+		Description string
+	}{
+		ID:          o.GetID(),
+		SecretKey:   o.GetSecretKey(),
+		Enabled:     o.GetEnabled(),
+		Description: o.GetDescription(),
+	}
+}
+
+// GetID returns value of ID
+func (o *LocalRouterPeer) GetID() types.ID {
+	return o.ID
+}
+
+// SetID sets value to ID
+func (o *LocalRouterPeer) SetID(v types.ID) {
+	o.ID = v
+}
+
+// GetSecretKey returns value of SecretKey
+func (o *LocalRouterPeer) GetSecretKey() string {
+	return o.SecretKey
+}
+
+// SetSecretKey sets value to SecretKey
+func (o *LocalRouterPeer) SetSecretKey(v string) {
+	o.SecretKey = v
+}
+
+// GetEnabled returns value of Enabled
+func (o *LocalRouterPeer) GetEnabled() bool {
+	return o.Enabled
+}
+
+// SetEnabled sets value to Enabled
+func (o *LocalRouterPeer) SetEnabled(v bool) {
+	o.Enabled = v
+}
+
+// GetDescription returns value of Description
+func (o *LocalRouterPeer) GetDescription() string {
+	return o.Description
+}
+
+// SetDescription sets value to Description
+func (o *LocalRouterPeer) SetDescription(v string) {
+	o.Description = v
+}
+
+/*************************************************
+* LocalRouterStaticRoute
+*************************************************/
+
+// LocalRouterStaticRoute represents API parameter/response structure
+type LocalRouterStaticRoute struct {
+	Prefix  string
+	NextHop string
+}
+
+// Validate validates by field tags
+func (o *LocalRouterStaticRoute) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterStaticRoute) setDefaults() interface{} {
+	return &struct {
+		Prefix  string
+		NextHop string
+	}{
+		Prefix:  o.GetPrefix(),
+		NextHop: o.GetNextHop(),
+	}
+}
+
+// GetPrefix returns value of Prefix
+func (o *LocalRouterStaticRoute) GetPrefix() string {
+	return o.Prefix
+}
+
+// SetPrefix sets value to Prefix
+func (o *LocalRouterStaticRoute) SetPrefix(v string) {
+	o.Prefix = v
+}
+
+// GetNextHop returns value of NextHop
+func (o *LocalRouterStaticRoute) GetNextHop() string {
+	return o.NextHop
+}
+
+// SetNextHop sets value to NextHop
+func (o *LocalRouterStaticRoute) SetNextHop(v string) {
+	o.NextHop = v
+}
+
+/*************************************************
+* LocalRouterCreateRequest
+*************************************************/
+
+// LocalRouterCreateRequest represents API parameter/response structure
+type LocalRouterCreateRequest struct {
+	Name        string `validate:"required"`
+	Description string `validate:"min=0,max=512"`
+	Tags        types.Tags
+	IconID      types.ID `mapconv:"Icon.ID"`
+}
+
+// Validate validates by field tags
+func (o *LocalRouterCreateRequest) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterCreateRequest) setDefaults() interface{} {
+	return &struct {
+		Name        string `validate:"required"`
+		Description string `validate:"min=0,max=512"`
+		Tags        types.Tags
+		IconID      types.ID `mapconv:"Icon.ID"`
+		Class       string   `mapconv:"Provider.Class"`
+	}{
+		Name:        o.GetName(),
+		Description: o.GetDescription(),
+		Tags:        o.GetTags(),
+		IconID:      o.GetIconID(),
+		Class:       "localrouter",
+	}
+}
+
+// GetName returns value of Name
+func (o *LocalRouterCreateRequest) GetName() string {
+	return o.Name
+}
+
+// SetName sets value to Name
+func (o *LocalRouterCreateRequest) SetName(v string) {
+	o.Name = v
+}
+
+// GetDescription returns value of Description
+func (o *LocalRouterCreateRequest) GetDescription() string {
+	return o.Description
+}
+
+// SetDescription sets value to Description
+func (o *LocalRouterCreateRequest) SetDescription(v string) {
+	o.Description = v
+}
+
+// GetTags returns value of Tags
+func (o *LocalRouterCreateRequest) GetTags() types.Tags {
+	return o.Tags
+}
+
+// SetTags sets value to Tags
+func (o *LocalRouterCreateRequest) SetTags(v types.Tags) {
+	o.Tags = v
+}
+
+// HasTag 指定のタグが存在する場合trueを返す
+func (o *LocalRouterCreateRequest) HasTag(tag string) bool {
+	return accessor.HasTag(o, tag)
+}
+
+// AppendTag 指定のタグを追加
+func (o *LocalRouterCreateRequest) AppendTag(tag string) {
+	accessor.AppendTag(o, tag)
+}
+
+// RemoveTag 指定のタグを削除
+func (o *LocalRouterCreateRequest) RemoveTag(tag string) {
+	accessor.RemoveTag(o, tag)
+}
+
+// ClearTags タグを全クリア
+func (o *LocalRouterCreateRequest) ClearTags() {
+	accessor.ClearTags(o)
+}
+
+// GetIconID returns value of IconID
+func (o *LocalRouterCreateRequest) GetIconID() types.ID {
+	return o.IconID
+}
+
+// SetIconID sets value to IconID
+func (o *LocalRouterCreateRequest) SetIconID(v types.ID) {
+	o.IconID = v
+}
+
+/*************************************************
+* LocalRouterUpdateRequest
+*************************************************/
+
+// LocalRouterUpdateRequest represents API parameter/response structure
+type LocalRouterUpdateRequest struct {
+	Switch       *LocalRouterSwitch        `mapconv:"Settings.LocalRouter.Switch,recursive"`
+	Interface    *LocalRouterInterface     `mapconv:"Settings.LocalRouter.Interface,recursive"`
+	Peers        []*LocalRouterPeer        `mapconv:"Settings.LocalRouter.[]Peers,recursive"`
+	StaticRoutes []*LocalRouterStaticRoute `mapconv:"Settings.LocalRouter.[]StaticRoutes,recursive"`
+	SettingsHash string                    `json:",omitempty" mapconv:",omitempty"`
+	Name         string                    `validate:"required"`
+	Description  string                    `validate:"min=0,max=512"`
+	Tags         types.Tags
+	IconID       types.ID `mapconv:"Icon.ID"`
+}
+
+// Validate validates by field tags
+func (o *LocalRouterUpdateRequest) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterUpdateRequest) setDefaults() interface{} {
+	return &struct {
+		Switch       *LocalRouterSwitch        `mapconv:"Settings.LocalRouter.Switch,recursive"`
+		Interface    *LocalRouterInterface     `mapconv:"Settings.LocalRouter.Interface,recursive"`
+		Peers        []*LocalRouterPeer        `mapconv:"Settings.LocalRouter.[]Peers,recursive"`
+		StaticRoutes []*LocalRouterStaticRoute `mapconv:"Settings.LocalRouter.[]StaticRoutes,recursive"`
+		SettingsHash string                    `json:",omitempty" mapconv:",omitempty"`
+		Name         string                    `validate:"required"`
+		Description  string                    `validate:"min=0,max=512"`
+		Tags         types.Tags
+		IconID       types.ID `mapconv:"Icon.ID"`
+	}{
+		Switch:       o.GetSwitch(),
+		Interface:    o.GetInterface(),
+		Peers:        o.GetPeers(),
+		StaticRoutes: o.GetStaticRoutes(),
+		SettingsHash: o.GetSettingsHash(),
+		Name:         o.GetName(),
+		Description:  o.GetDescription(),
+		Tags:         o.GetTags(),
+		IconID:       o.GetIconID(),
+	}
+}
+
+// GetSwitch returns value of Switch
+func (o *LocalRouterUpdateRequest) GetSwitch() *LocalRouterSwitch {
+	return o.Switch
+}
+
+// SetSwitch sets value to Switch
+func (o *LocalRouterUpdateRequest) SetSwitch(v *LocalRouterSwitch) {
+	o.Switch = v
+}
+
+// GetInterface returns value of Interface
+func (o *LocalRouterUpdateRequest) GetInterface() *LocalRouterInterface {
+	return o.Interface
+}
+
+// SetInterface sets value to Interface
+func (o *LocalRouterUpdateRequest) SetInterface(v *LocalRouterInterface) {
+	o.Interface = v
+}
+
+// GetPeers returns value of Peers
+func (o *LocalRouterUpdateRequest) GetPeers() []*LocalRouterPeer {
+	return o.Peers
+}
+
+// SetPeers sets value to Peers
+func (o *LocalRouterUpdateRequest) SetPeers(v []*LocalRouterPeer) {
+	o.Peers = v
+}
+
+// GetStaticRoutes returns value of StaticRoutes
+func (o *LocalRouterUpdateRequest) GetStaticRoutes() []*LocalRouterStaticRoute {
+	return o.StaticRoutes
+}
+
+// SetStaticRoutes sets value to StaticRoutes
+func (o *LocalRouterUpdateRequest) SetStaticRoutes(v []*LocalRouterStaticRoute) {
+	o.StaticRoutes = v
+}
+
+// GetSettingsHash returns value of SettingsHash
+func (o *LocalRouterUpdateRequest) GetSettingsHash() string {
+	return o.SettingsHash
+}
+
+// SetSettingsHash sets value to SettingsHash
+func (o *LocalRouterUpdateRequest) SetSettingsHash(v string) {
+	o.SettingsHash = v
+}
+
+// GetName returns value of Name
+func (o *LocalRouterUpdateRequest) GetName() string {
+	return o.Name
+}
+
+// SetName sets value to Name
+func (o *LocalRouterUpdateRequest) SetName(v string) {
+	o.Name = v
+}
+
+// GetDescription returns value of Description
+func (o *LocalRouterUpdateRequest) GetDescription() string {
+	return o.Description
+}
+
+// SetDescription sets value to Description
+func (o *LocalRouterUpdateRequest) SetDescription(v string) {
+	o.Description = v
+}
+
+// GetTags returns value of Tags
+func (o *LocalRouterUpdateRequest) GetTags() types.Tags {
+	return o.Tags
+}
+
+// SetTags sets value to Tags
+func (o *LocalRouterUpdateRequest) SetTags(v types.Tags) {
+	o.Tags = v
+}
+
+// HasTag 指定のタグが存在する場合trueを返す
+func (o *LocalRouterUpdateRequest) HasTag(tag string) bool {
+	return accessor.HasTag(o, tag)
+}
+
+// AppendTag 指定のタグを追加
+func (o *LocalRouterUpdateRequest) AppendTag(tag string) {
+	accessor.AppendTag(o, tag)
+}
+
+// RemoveTag 指定のタグを削除
+func (o *LocalRouterUpdateRequest) RemoveTag(tag string) {
+	accessor.RemoveTag(o, tag)
+}
+
+// ClearTags タグを全クリア
+func (o *LocalRouterUpdateRequest) ClearTags() {
+	accessor.ClearTags(o)
+}
+
+// GetIconID returns value of IconID
+func (o *LocalRouterUpdateRequest) GetIconID() types.ID {
+	return o.IconID
+}
+
+// SetIconID sets value to IconID
+func (o *LocalRouterUpdateRequest) SetIconID(v types.ID) {
+	o.IconID = v
+}
+
+/*************************************************
+* LocalRouterUpdateSettingsRequest
+*************************************************/
+
+// LocalRouterUpdateSettingsRequest represents API parameter/response structure
+type LocalRouterUpdateSettingsRequest struct {
+	Switch       *LocalRouterSwitch        `mapconv:"Settings.LocalRouter.Switch,recursive"`
+	Interface    *LocalRouterInterface     `mapconv:"Settings.LocalRouter.Interface,recursive"`
+	Peers        []*LocalRouterPeer        `mapconv:"Settings.LocalRouter.[]Peers,recursive"`
+	StaticRoutes []*LocalRouterStaticRoute `mapconv:"Settings.LocalRouter.[]StaticRoutes,recursive"`
+	SettingsHash string                    `json:",omitempty" mapconv:",omitempty"`
+}
+
+// Validate validates by field tags
+func (o *LocalRouterUpdateSettingsRequest) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterUpdateSettingsRequest) setDefaults() interface{} {
+	return &struct {
+		Switch       *LocalRouterSwitch        `mapconv:"Settings.LocalRouter.Switch,recursive"`
+		Interface    *LocalRouterInterface     `mapconv:"Settings.LocalRouter.Interface,recursive"`
+		Peers        []*LocalRouterPeer        `mapconv:"Settings.LocalRouter.[]Peers,recursive"`
+		StaticRoutes []*LocalRouterStaticRoute `mapconv:"Settings.LocalRouter.[]StaticRoutes,recursive"`
+		SettingsHash string                    `json:",omitempty" mapconv:",omitempty"`
+	}{
+		Switch:       o.GetSwitch(),
+		Interface:    o.GetInterface(),
+		Peers:        o.GetPeers(),
+		StaticRoutes: o.GetStaticRoutes(),
+		SettingsHash: o.GetSettingsHash(),
+	}
+}
+
+// GetSwitch returns value of Switch
+func (o *LocalRouterUpdateSettingsRequest) GetSwitch() *LocalRouterSwitch {
+	return o.Switch
+}
+
+// SetSwitch sets value to Switch
+func (o *LocalRouterUpdateSettingsRequest) SetSwitch(v *LocalRouterSwitch) {
+	o.Switch = v
+}
+
+// GetInterface returns value of Interface
+func (o *LocalRouterUpdateSettingsRequest) GetInterface() *LocalRouterInterface {
+	return o.Interface
+}
+
+// SetInterface sets value to Interface
+func (o *LocalRouterUpdateSettingsRequest) SetInterface(v *LocalRouterInterface) {
+	o.Interface = v
+}
+
+// GetPeers returns value of Peers
+func (o *LocalRouterUpdateSettingsRequest) GetPeers() []*LocalRouterPeer {
+	return o.Peers
+}
+
+// SetPeers sets value to Peers
+func (o *LocalRouterUpdateSettingsRequest) SetPeers(v []*LocalRouterPeer) {
+	o.Peers = v
+}
+
+// GetStaticRoutes returns value of StaticRoutes
+func (o *LocalRouterUpdateSettingsRequest) GetStaticRoutes() []*LocalRouterStaticRoute {
+	return o.StaticRoutes
+}
+
+// SetStaticRoutes sets value to StaticRoutes
+func (o *LocalRouterUpdateSettingsRequest) SetStaticRoutes(v []*LocalRouterStaticRoute) {
+	o.StaticRoutes = v
+}
+
+// GetSettingsHash returns value of SettingsHash
+func (o *LocalRouterUpdateSettingsRequest) GetSettingsHash() string {
+	return o.SettingsHash
+}
+
+// SetSettingsHash sets value to SettingsHash
+func (o *LocalRouterUpdateSettingsRequest) SetSettingsHash(v string) {
+	o.SettingsHash = v
+}
+
+/*************************************************
+* LocalRouterHealth
+*************************************************/
+
+// LocalRouterHealth represents API parameter/response structure
+type LocalRouterHealth struct {
+	Peers []*LocalRouterHealthPeer `mapconv:"LocalRouter.[]Peers,recursive"`
+}
+
+// Validate validates by field tags
+func (o *LocalRouterHealth) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterHealth) setDefaults() interface{} {
+	return &struct {
+		Peers []*LocalRouterHealthPeer `mapconv:"LocalRouter.[]Peers,recursive"`
+	}{
+		Peers: o.GetPeers(),
+	}
+}
+
+// GetPeers returns value of Peers
+func (o *LocalRouterHealth) GetPeers() []*LocalRouterHealthPeer {
+	return o.Peers
+}
+
+// SetPeers sets value to Peers
+func (o *LocalRouterHealth) SetPeers(v []*LocalRouterHealthPeer) {
+	o.Peers = v
+}
+
+/*************************************************
+* LocalRouterHealthPeer
+*************************************************/
+
+// LocalRouterHealthPeer represents API parameter/response structure
+type LocalRouterHealthPeer struct {
+	ID     types.ID
+	Status types.EServerInstanceStatus
+	Routes []string
+}
+
+// Validate validates by field tags
+func (o *LocalRouterHealthPeer) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterHealthPeer) setDefaults() interface{} {
+	return &struct {
+		ID     types.ID
+		Status types.EServerInstanceStatus
+		Routes []string
+	}{
+		ID:     o.GetID(),
+		Status: o.GetStatus(),
+		Routes: o.GetRoutes(),
+	}
+}
+
+// GetID returns value of ID
+func (o *LocalRouterHealthPeer) GetID() types.ID {
+	return o.ID
+}
+
+// SetID sets value to ID
+func (o *LocalRouterHealthPeer) SetID(v types.ID) {
+	o.ID = v
+}
+
+// GetStatus returns value of Status
+func (o *LocalRouterHealthPeer) GetStatus() types.EServerInstanceStatus {
+	return o.Status
+}
+
+// SetStatus sets value to Status
+func (o *LocalRouterHealthPeer) SetStatus(v types.EServerInstanceStatus) {
+	o.Status = v
+}
+
+// GetRoutes returns value of Routes
+func (o *LocalRouterHealthPeer) GetRoutes() []string {
+	return o.Routes
+}
+
+// SetRoutes sets value to Routes
+func (o *LocalRouterHealthPeer) SetRoutes(v []string) {
+	o.Routes = v
+}
+
+/*************************************************
+* LocalRouterActivity
+*************************************************/
+
+// LocalRouterActivity represents API parameter/response structure
+type LocalRouterActivity struct {
+	Values []*MonitorLocalRouterValue `mapconv:"[]LocalRouter"`
+}
+
+// Validate validates by field tags
+func (o *LocalRouterActivity) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *LocalRouterActivity) setDefaults() interface{} {
+	return &struct {
+		Values []*MonitorLocalRouterValue `mapconv:"[]LocalRouter"`
+	}{
+		Values: o.GetValues(),
+	}
+}
+
+// GetValues returns value of Values
+func (o *LocalRouterActivity) GetValues() []*MonitorLocalRouterValue {
+	return o.Values
+}
+
+// SetValues sets value to Values
+func (o *LocalRouterActivity) SetValues(v []*MonitorLocalRouterValue) {
+	o.Values = v
+}
+
+/*************************************************
+* MonitorLocalRouterValue
+*************************************************/
+
+// MonitorLocalRouterValue represents API parameter/response structure
+type MonitorLocalRouterValue struct {
+	Time               time.Time `json:",omitempty" mapconv:",omitempty"`
+	ReceiveBytesPerSec float64   `json:",omitempty" mapconv:",omitempty"`
+	SendBytesPerSec    float64   `json:",omitempty" mapconv:",omitempty"`
+}
+
+// Validate validates by field tags
+func (o *MonitorLocalRouterValue) Validate() error {
+	return validator.New().Struct(o)
+}
+
+// setDefaults implements sacloud.argumentDefaulter
+func (o *MonitorLocalRouterValue) setDefaults() interface{} {
+	return &struct {
+		Time               time.Time `json:",omitempty" mapconv:",omitempty"`
+		ReceiveBytesPerSec float64   `json:",omitempty" mapconv:",omitempty"`
+		SendBytesPerSec    float64   `json:",omitempty" mapconv:",omitempty"`
+	}{
+		Time:               o.GetTime(),
+		ReceiveBytesPerSec: o.GetReceiveBytesPerSec(),
+		SendBytesPerSec:    o.GetSendBytesPerSec(),
+	}
+}
+
+// GetTime returns value of Time
+func (o *MonitorLocalRouterValue) GetTime() time.Time {
+	return o.Time
+}
+
+// SetTime sets value to Time
+func (o *MonitorLocalRouterValue) SetTime(v time.Time) {
+	o.Time = v
+}
+
+// GetReceiveBytesPerSec returns value of ReceiveBytesPerSec
+func (o *MonitorLocalRouterValue) GetReceiveBytesPerSec() float64 {
+	return o.ReceiveBytesPerSec
+}
+
+// SetReceiveBytesPerSec sets value to ReceiveBytesPerSec
+func (o *MonitorLocalRouterValue) SetReceiveBytesPerSec(v float64) {
+	o.ReceiveBytesPerSec = v
+}
+
+// GetSendBytesPerSec returns value of SendBytesPerSec
+func (o *MonitorLocalRouterValue) GetSendBytesPerSec() float64 {
+	return o.SendBytesPerSec
+}
+
+// SetSendBytesPerSec sets value to SendBytesPerSec
+func (o *MonitorLocalRouterValue) SetSendBytesPerSec(v float64) {
+	o.SendBytesPerSec = v
+}
+
 /*************************************************
 * MobileGateway
 *************************************************/
@@ -22911,19 +23922,20 @@ func (o *SubnetIPAddress) SetIPAddress(v string) {
 
 // Switch represents API parameter/response structure
 type Switch struct {
-	ID             types.ID
-	Name           string `validate:"required"`
-	Description    string `validate:"min=0,max=512"`
-	Tags           types.Tags
-	IconID         types.ID `mapconv:"Icon.ID"`
-	CreatedAt      time.Time
-	ModifiedAt     time.Time
-	Scope          types.EScope
-	ServerCount    int
-	NetworkMaskLen int             `mapconv:"UserSubnet.NetworkMaskLen" validate:"min=1,max=32"`
-	DefaultRoute   string          `mapconv:"UserSubnet.DefaultRoute" validate:"ipv4"`
-	Subnets        []*SwitchSubnet `json:",omitempty" mapconv:"[]Subnets,omitempty,recursive"`
-	BridgeID       types.ID        `mapconv:"Bridge.ID,omitempty"`
+	ID                 types.ID
+	Name               string `validate:"required"`
+	Description        string `validate:"min=0,max=512"`
+	Tags               types.Tags
+	IconID             types.ID `mapconv:"Icon.ID"`
+	CreatedAt          time.Time
+	ModifiedAt         time.Time
+	Scope              types.EScope
+	ServerCount        int
+	NetworkMaskLen     int             `mapconv:"UserSubnet.NetworkMaskLen" validate:"min=1,max=32"`
+	DefaultRoute       string          `mapconv:"UserSubnet.DefaultRoute" validate:"ipv4"`
+	Subnets            []*SwitchSubnet `json:",omitempty" mapconv:"[]Subnets,omitempty,recursive"`
+	BridgeID           types.ID        `mapconv:"Bridge.ID,omitempty"`
+	HybridConnectionID types.ID        `mapconv:"HybridConnection.ID,omitempty"`
 }
 
 // Validate validates by field tags
@@ -22934,33 +23946,35 @@ func (o *Switch) Validate() error {
 // setDefaults implements sacloud.argumentDefaulter
 func (o *Switch) setDefaults() interface{} {
 	return &struct {
-		ID             types.ID
-		Name           string `validate:"required"`
-		Description    string `validate:"min=0,max=512"`
-		Tags           types.Tags
-		IconID         types.ID `mapconv:"Icon.ID"`
-		CreatedAt      time.Time
-		ModifiedAt     time.Time
-		Scope          types.EScope
-		ServerCount    int
-		NetworkMaskLen int             `mapconv:"UserSubnet.NetworkMaskLen" validate:"min=1,max=32"`
-		DefaultRoute   string          `mapconv:"UserSubnet.DefaultRoute" validate:"ipv4"`
-		Subnets        []*SwitchSubnet `json:",omitempty" mapconv:"[]Subnets,omitempty,recursive"`
-		BridgeID       types.ID        `mapconv:"Bridge.ID,omitempty"`
+		ID                 types.ID
+		Name               string `validate:"required"`
+		Description        string `validate:"min=0,max=512"`
+		Tags               types.Tags
+		IconID             types.ID `mapconv:"Icon.ID"`
+		CreatedAt          time.Time
+		ModifiedAt         time.Time
+		Scope              types.EScope
+		ServerCount        int
+		NetworkMaskLen     int             `mapconv:"UserSubnet.NetworkMaskLen" validate:"min=1,max=32"`
+		DefaultRoute       string          `mapconv:"UserSubnet.DefaultRoute" validate:"ipv4"`
+		Subnets            []*SwitchSubnet `json:",omitempty" mapconv:"[]Subnets,omitempty,recursive"`
+		BridgeID           types.ID        `mapconv:"Bridge.ID,omitempty"`
+		HybridConnectionID types.ID        `mapconv:"HybridConnection.ID,omitempty"`
 	}{
-		ID:             o.GetID(),
-		Name:           o.GetName(),
-		Description:    o.GetDescription(),
-		Tags:           o.GetTags(),
-		IconID:         o.GetIconID(),
-		CreatedAt:      o.GetCreatedAt(),
-		ModifiedAt:     o.GetModifiedAt(),
-		Scope:          o.GetScope(),
-		ServerCount:    o.GetServerCount(),
-		NetworkMaskLen: o.GetNetworkMaskLen(),
-		DefaultRoute:   o.GetDefaultRoute(),
-		Subnets:        o.GetSubnets(),
-		BridgeID:       o.GetBridgeID(),
+		ID:                 o.GetID(),
+		Name:               o.GetName(),
+		Description:        o.GetDescription(),
+		Tags:               o.GetTags(),
+		IconID:             o.GetIconID(),
+		CreatedAt:          o.GetCreatedAt(),
+		ModifiedAt:         o.GetModifiedAt(),
+		Scope:              o.GetScope(),
+		ServerCount:        o.GetServerCount(),
+		NetworkMaskLen:     o.GetNetworkMaskLen(),
+		DefaultRoute:       o.GetDefaultRoute(),
+		Subnets:            o.GetSubnets(),
+		BridgeID:           o.GetBridgeID(),
+		HybridConnectionID: o.GetHybridConnectionID(),
 	}
 }
 
@@ -23134,6 +24148,16 @@ func (o *Switch) SetBridgeID(v types.ID) {
 	o.BridgeID = v
 }
 
+// GetHybridConnectionID returns value of HybridConnectionID
+func (o *Switch) GetHybridConnectionID() types.ID {
+	return o.HybridConnectionID
+}
+
+// SetHybridConnectionID sets value to HybridConnectionID
+func (o *Switch) SetHybridConnectionID(v types.ID) {
+	o.HybridConnectionID = v
+}
+
 /*************************************************
 * SwitchSubnet
 *************************************************/
diff --git a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_result.go b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_result.go
index 056583c7..b2894e22 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_result.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/sacloud/zz_result.go
@@ -807,6 +807,57 @@ type LoadBalancerStatusResult struct {
 	Status []*LoadBalancerStatus `json:",omitempty" mapconv:"[]LoadBalancer,omitempty,recursive"`
 }
 
+// LocalRouterFindResult represents the Result of API
+type LocalRouterFindResult struct {
+	Total int `json:",omitempty"` // Total count of target resources
+	From  int `json:",omitempty"` // Current page number
+	Count int `json:",omitempty"` // Count of current page
+
+	LocalRouters []*LocalRouter `json:",omitempty" mapconv:"[]CommonServiceItems,omitempty,recursive"`
+}
+
+// localRouterCreateResult represents the Result of API
+type localRouterCreateResult struct {
+	IsOk bool `json:",omitempty"` // is_ok
+
+	LocalRouter *LocalRouter `json:",omitempty" mapconv:"CommonServiceItem,omitempty,recursive"`
+}
+
+// localRouterReadResult represents the Result of API
+type localRouterReadResult struct {
+	IsOk bool `json:",omitempty"` // is_ok
+
+	LocalRouter *LocalRouter `json:",omitempty" mapconv:"CommonServiceItem,omitempty,recursive"`
+}
+
+// localRouterUpdateResult represents the Result of API
+type localRouterUpdateResult struct {
+	IsOk bool `json:",omitempty"` // is_ok
+
+	LocalRouter *LocalRouter `json:",omitempty" mapconv:"CommonServiceItem,omitempty,recursive"`
+}
+
+// localRouterUpdateSettingsResult represents the Result of API
+type localRouterUpdateSettingsResult struct {
+	IsOk bool `json:",omitempty"` // is_ok
+
+	LocalRouter *LocalRouter `json:",omitempty" mapconv:"CommonServiceItem,omitempty,recursive"`
+}
+
+// localRouterHealthStatusResult represents the Result of API
+type localRouterHealthStatusResult struct {
+	IsOk bool `json:",omitempty"` // is_ok
+
+	LocalRouterHealth *LocalRouterHealth `json:",omitempty" mapconv:"LocalRouter,omitempty,recursive"`
+}
+
+// localRouterMonitorLocalRouterResult represents the Result of API
+type localRouterMonitorLocalRouterResult struct {
+	IsOk bool `json:",omitempty"` // is_ok
+
+	LocalRouterActivity *LocalRouterActivity `json:",omitempty" mapconv:"Data,omitempty,recursive"`
+}
+
 // MobileGatewayFindResult represents the Result of API
 type MobileGatewayFindResult struct {
 	Total int `json:",omitempty"` // Total count of target resources
diff --git a/vendor/github.com/sacloud/libsacloud/v2/utils/builder/localrouter/api_client.go b/vendor/github.com/sacloud/libsacloud/v2/utils/builder/localrouter/api_client.go
new file mode 100644
index 00000000..4378d04b
--- /dev/null
+++ b/vendor/github.com/sacloud/libsacloud/v2/utils/builder/localrouter/api_client.go
@@ -0,0 +1,29 @@
+// Copyright 2016-2020 The Libsacloud Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package localrouter
+
+import "github.com/sacloud/libsacloud/v2/sacloud"
+
+// APIClient builderが利用するAPIクライアント
+type APIClient struct {
+	LocalRouter sacloud.LocalRouterAPI
+}
+
+// NewAPIClient builderが利用するAPIクライアントを返す
+func NewAPIClient(caller sacloud.APICaller) *APIClient {
+	return &APIClient{
+		LocalRouter: sacloud.NewLocalRouterOp(caller),
+	}
+}
diff --git a/vendor/github.com/sacloud/libsacloud/v2/utils/builder/localrouter/builder.go b/vendor/github.com/sacloud/libsacloud/v2/utils/builder/localrouter/builder.go
new file mode 100644
index 00000000..e223f9eb
--- /dev/null
+++ b/vendor/github.com/sacloud/libsacloud/v2/utils/builder/localrouter/builder.go
@@ -0,0 +1,124 @@
+// Copyright 2016-2020 The Libsacloud Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package localrouter
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/sacloud/libsacloud/v2/sacloud"
+	"github.com/sacloud/libsacloud/v2/sacloud/types"
+)
+
+// Builder ローカルルータの構築を行う
+type Builder struct {
+	Name        string
+	Description string
+	Tags        types.Tags
+	IconID      types.ID
+
+	Switch       *sacloud.LocalRouterSwitch
+	Interface    *sacloud.LocalRouterInterface
+	Peers        []*sacloud.LocalRouterPeer
+	StaticRoutes []*sacloud.LocalRouterStaticRoute
+
+	Client *APIClient
+}
+
+// Validate 設定値の検証
+func (b *Builder) Validate(ctx context.Context) error {
+	requiredValues := map[string]bool{
+		"Switch":    b.Switch == nil,
+		"Interface": b.Interface == nil,
+	}
+	for key, empty := range requiredValues {
+		if empty {
+			return fmt.Errorf("%s is required", key)
+		}
+	}
+	return nil
+}
+
+// Build ローカルルータの作成や設定をまとめて行う
+func (b *Builder) Build(ctx context.Context) (*sacloud.LocalRouter, error) {
+	if err := b.Validate(ctx); err != nil {
+		return nil, err
+	}
+
+	localRouter, err := b.Client.LocalRouter.Create(ctx, &sacloud.LocalRouterCreateRequest{
+		Name:        b.Name,
+		Description: b.Description,
+		Tags:        b.Tags,
+		IconID:      b.IconID,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	localRouter, err = b.Client.LocalRouter.UpdateSettings(ctx, localRouter.ID, &sacloud.LocalRouterUpdateSettingsRequest{
+		Switch:       b.Switch,
+		Interface:    b.Interface,
+		StaticRoutes: b.StaticRoutes,
+		SettingsHash: localRouter.SettingsHash,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	if len(b.Peers) > 0 {
+		localRouter, err = b.Client.LocalRouter.UpdateSettings(ctx, localRouter.ID, &sacloud.LocalRouterUpdateSettingsRequest{
+			Switch:       localRouter.Switch,
+			Interface:    localRouter.Interface,
+			StaticRoutes: localRouter.StaticRoutes,
+			Peers:        b.Peers,
+			SettingsHash: localRouter.SettingsHash,
+		})
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return localRouter, nil
+}
+
+// Update ローカルルータの更新
+func (b *Builder) Update(ctx context.Context, id types.ID) (*sacloud.LocalRouter, error) {
+	if err := b.Validate(ctx); err != nil {
+		return nil, err
+	}
+
+	// check Internet is exists
+	localRouter, err := b.Client.LocalRouter.Read(ctx, id)
+	if err != nil {
+		return nil, err
+	}
+
+	localRouter, err = b.Client.LocalRouter.Update(ctx, id, &sacloud.LocalRouterUpdateRequest{
+		Switch:       b.Switch,
+		Interface:    b.Interface,
+		Peers:        b.Peers,
+		StaticRoutes: b.StaticRoutes,
+		SettingsHash: localRouter.SettingsHash,
+		Name:         b.Name,
+		Description:  b.Description,
+		Tags:         b.Tags,
+		IconID:       b.IconID,
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	return localRouter, nil
+}
diff --git a/vendor/github.com/sacloud/libsacloud/v2/version.go b/vendor/github.com/sacloud/libsacloud/v2/version.go
index 7badb9ee..ea051d85 100644
--- a/vendor/github.com/sacloud/libsacloud/v2/version.go
+++ b/vendor/github.com/sacloud/libsacloud/v2/version.go
@@ -15,4 +15,4 @@
 package libsacloud
 
 // Version バージョン
-const Version = "2.0.0-rc3"
+const Version = "2.0.0-rc5"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 979fd0df..598fb44c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -228,7 +228,7 @@ github.com/posener/complete/match
 github.com/sacloud/ftps
 # github.com/sacloud/iso9660wrap v0.0.0-20171031075302-eda21f77f6a8
 github.com/sacloud/iso9660wrap
-# github.com/sacloud/libsacloud/v2 v2.0.0-rc3
+# github.com/sacloud/libsacloud/v2 v2.0.0-rc5
 github.com/sacloud/libsacloud/v2
 github.com/sacloud/libsacloud/v2/pkg/cidr
 github.com/sacloud/libsacloud/v2/pkg/mapconv
@@ -248,6 +248,7 @@ github.com/sacloud/libsacloud/v2/utils/builder
 github.com/sacloud/libsacloud/v2/utils/builder/database
 github.com/sacloud/libsacloud/v2/utils/builder/disk
 github.com/sacloud/libsacloud/v2/utils/builder/internet
+github.com/sacloud/libsacloud/v2/utils/builder/localrouter
 github.com/sacloud/libsacloud/v2/utils/builder/mobilegateway
 github.com/sacloud/libsacloud/v2/utils/builder/registry
 github.com/sacloud/libsacloud/v2/utils/builder/server
diff --git a/website/docs/d/local_router.md b/website/docs/d/local_router.md
new file mode 100644
index 00000000..3d79f64b
--- /dev/null
+++ b/website/docs/d/local_router.md
@@ -0,0 +1,91 @@
+---
+layout: "sakuracloud"
+page_title: "SakuraCloud: sakuracloud_local_router"
+subcategory: "Networking"
+description: |-
+  Get information about an existing Local Router.
+---
+
+# Data Source: sakuracloud_local_router
+
+Get information about an existing Local Router.
+
+## Example Usage
+
+```hcl
+data "sakuracloud_local_router" "foobar" {
+  filter {
+    names = ["foobar"]
+  }
+}
+```
+
+## Argument Reference
+
+* `filter` - (Optional) One or more values used for filtering, as defined below.
+
+---
+
+A `filter` block supports the following:
+
+* `condition` - (Optional) One or more name/values pairs used for filtering. There are several valid keys, for a full reference, check out finding section in the [SakuraCloud API reference](https://developer.sakura.ad.jp/cloud/api/1.1/).
+* `id` - (Optional) The resource id on SakuraCloud used for filtering.
+* `names` - (Optional) The resource names on SakuraCloud used for filtering. If multiple values ​​are specified, they combined as AND condition.
+* `tags` - (Optional) The resource tags on SakuraCloud used for filtering. If multiple values ​​are specified, they combined as AND condition.
+
+---
+
+A `condition` block supports the following:
+
+* `name` - (Required) The name of the target field. This value is case-sensitive.
+* `values` - (Required) The values of the condition. If multiple values ​​are specified, they combined as AND condition.
+
+
+## Attribute Reference
+
+* `id` - The id of the Local Router.
+* `description` - The description of the LocalRouter.
+* `icon_id` - The icon id attached to the LocalRouter.
+* `name` - The name of the LocalRouter.
+* `network_interface` - A list of `network_interface` blocks as defined below.
+* `peer` - A list of `peer` blocks as defined below.
+* `secret_keys` - A list of secret key used for peering from other LocalRouters.
+* `static_route` - A list of `static_route` blocks as defined below.
+* `switch` - A list of `switch` blocks as defined below.
+* `tags` - Any tags assigned to the LocalRouter.
+
+
+---
+
+A `network_interface` block exports the following:
+
+* `ip_addresses` - The list of IP address assigned to the LocalRouter.
+* `netmask` - The bit length of the subnet assigned to the LocalRouter.
+* `vip` - The virtual IP address.
+* `vrid` - The Virtual Router Identifier.
+
+---
+
+A `peer` block exports the following:
+
+* `description` - The description of the LocalRouter.
+* `enabled` - The flag to enable the LocalRouter.
+* `peer_id` - The ID of the peer LocalRouter.
+* `secret_key` - The secret key of the peer LocalRouter.
+
+---
+
+A `static_route` block exports the following:
+
+* `next_hop` - The IP address of the next hop.
+* `prefix` - The CIDR block of destination.
+
+---
+
+A `switch` block exports the following:
+
+* `category` - The category name of connected services (e.g. `cloud`, `vps`).
+* `code` - The resource ID of the Switch.
+* `zone_id` - The id of the Zone.
+
+
diff --git a/website/docs/r/local_router.md b/website/docs/r/local_router.md
new file mode 100644
index 00000000..9e726742
--- /dev/null
+++ b/website/docs/r/local_router.md
@@ -0,0 +1,121 @@
+---
+layout: "sakuracloud"
+page_title: "SakuraCloud: sakuracloud_local_router"
+subcategory: "Networking"
+description: |-
+  Manages a SakuraCloud Local Router.
+---
+
+# sakuracloud_local_router
+
+Manages a SakuraCloud Local Router.
+
+## Example Usage
+
+```hcl
+resource "sakuracloud_local_router" "foobar" {
+  name        = "example"
+  description = "descriptio"
+  tags        = ["tag1", "tag2"]
+
+  switch {
+    code     = sakuracloud_switch.foobar.id
+    category = "cloud"
+    zone_id  = "is1a"
+  }
+
+  network_interface {
+    vip          = "192.168.11.1"
+    ip_addresses = ["192.168.11.11", "192.168.11.12"]
+    netmask      = 24
+    vrid         = 101
+  }
+
+  static_route {
+    prefix   = "10.0.0.0/24"
+    next_hop = "192.168.11.2"
+  }
+  static_route {
+    prefix   = "172.16.0.0/16"
+    next_hop = "192.168.11.3"
+  }
+
+  peer {
+    peer_id     = data.sakuracloud_local_router.peer.id
+    secret_key  = data.sakuracloud_local_router.secret_keys[0]
+    description = "description"
+  }
+}
+
+resource "sakuracloud_switch" "foobar" {
+  name = "example"
+}
+
+data "sakuracloud_local_router" "peer" {
+  filter {
+    names = ["peer"]
+  }
+}
+
+```
+## Argument Reference
+
+* `name` - (Required) The name of the LocalRouter. The length of this value must be in the range [`1`-`64`].
+* `network_interface` - (Required) An `network_interface` block as defined below.
+* `switch` - (Required) A `switch` block as defined below.
+* `peer` - (Optional) One or more `peer` blocks as defined below.
+* `static_route` - (Optional) One or more `static_route` blocks as defined below.
+
+#### Common Arguments
+
+* `description` - (Optional) The description of the LoadBalancer. The length of this value must be in the range [`1`-`512`].
+* `icon_id` - (Optional) The icon id to attach to the LoadBalancer.
+* `tags` - (Optional) Any tags to assign to the LoadBalancer.
+
+---
+
+A `network_interface` block supports the following:
+
+* `ip_addresses` - (Required) A list of IP address to assign to the LocalRouter.
+* `netmask` - (Required) The bit length of the subnet assigned to the LocalRouter. This must be in the range [`8`-`29`].
+* `vip` - (Required) The virtual IP address.
+* `vrid` - (Required) The Virtual Router Identifier.
+
+---
+
+A `peer` block supports the following:
+
+* `peer_id` - (Required) The ID of the peer LocalRouter.
+* `secret_key` - (Required) The secret key of the peer LocalRouter.
+* `description` - (Optional) The description of the LocalRouter. The length of this value must be in the range [`1`-`512`].
+* `enabled` - (Optional) The flag to enable the LocalRouter.
+
+---
+
+A `static_route` block supports the following:
+
+* `next_hop` - (Required) The IP address of the next hop.
+* `prefix` - (Required) The CIDR block of destination.
+
+---
+
+A `switch` block supports the following:
+
+* `category` - (Optional) The category name of connected services (e.g. `cloud`, `vps`).
+* `code` - (Required) The resource ID of the Switch.
+* `zone_id` - (Required) The id of the Zone.
+
+
+### Timeouts
+
+The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#operation-timeouts) for certain actions:
+
+* `create` - (Defaults to 20 minutes) Used when creating the Local Router
+* `update` - (Defaults to 20 minutes) Used when updating the Local Router
+* `delete` - (Defaults to 20 minutes) Used when deleting Local Router
+
+## Attribute Reference
+
+* `id` - The id of the Local Router.
+* `secret_keys` - A list of secret key used for peering from other LocalRouters.
+
diff --git a/website/sakuracloud.erb b/website/sakuracloud.erb
index 19d47314..127dfd44 100644
--- a/website/sakuracloud.erb
+++ b/website/sakuracloud.erb
@@ -107,6 +107,9 @@
                 <li>
                   <a href="/docs/providers/sakuracloud/d/internet.html">sakuracloud_internet</a>
                 </li>
+                <li>
+                  <a href="/docs/providers/sakuracloud/d/local_router.html">sakuracloud_local_router</a>
+                </li>
                 <li>
                   <a href="/docs/providers/sakuracloud/d/packet_filter.html">sakuracloud_packet_filter</a>
                 </li>
@@ -132,6 +135,9 @@
                 <li>
                   <a href="/docs/providers/sakuracloud/r/ipv4_ptr.html">sakuracloud_ipv4_ptr</a>
                 </li>
+                <li>
+                  <a href="/docs/providers/sakuracloud/r/local_router.html">sakuracloud_local_router</a>
+                </li>
                 <li>
                   <a href="/docs/providers/sakuracloud/r/packet_filter.html">sakuracloud_packet_filter</a>
                 </li>


Test patch:
diff --git a/sakuracloud/data_source_sakuracloud_local_router_test.go b/sakuracloud/data_source_sakuracloud_local_router_test.go
new file mode 100644
index 00000000..eabe63a8
--- /dev/null
+++ b/sakuracloud/data_source_sakuracloud_local_router_test.go
@@ -0,0 +1,89 @@
+// Copyright 2016-2020 terraform-provider-sakuracloud authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sakuracloud
+
+import (
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
+)
+
+func TestAccSakuraCloudDataSourceLocalRouter_basic(t *testing.T) {
+	resourceName := "data.sakuracloud_local_router.foobar"
+	rand := randomName()
+
+	resource.ParallelTest(t, resource.TestCase{
+		PreCheck:  func() { testAccPreCheck(t) },
+		Providers: testAccProviders,
+		Steps: []resource.TestStep{
+			{
+				Config: buildConfigWithArgs(testAccSakuraCloudDataSourceLocalRouter_basic, rand),
+				Check: resource.ComposeTestCheckFunc(
+					testCheckSakuraCloudDataSourceExists(resourceName),
+					resource.TestCheckResourceAttr(resourceName, "name", rand),
+					resource.TestCheckResourceAttr(resourceName, "description", "description"),
+					resource.TestCheckResourceAttr(resourceName, "tags.#", "3"),
+					resource.TestCheckResourceAttr(resourceName, "tags.4151227546", "tag1"),
+					resource.TestCheckResourceAttr(resourceName, "tags.1852302624", "tag2"),
+					resource.TestCheckResourceAttr(resourceName, "tags.425776566", "tag3"),
+					resource.TestCheckResourceAttrPair(
+						resourceName, "switch.0.code",
+						"sakuracloud_switch.foobar", "id"),
+					resource.TestCheckResourceAttr(resourceName, "switch.0.category", "cloud"),
+					resource.TestCheckResourceAttrPair(
+						resourceName, "switch.0.zone_id",
+						"data.sakuracloud_zone.current", "name"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.vip", "192.168.11.1"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.ip_addresses.#", "2"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.ip_addresses.0", "192.168.11.11"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.ip_addresses.1", "192.168.11.12"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.netmask", "24"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.vrid", "1"),
+				),
+			},
+		},
+	})
+}
+
+var testAccSakuraCloudDataSourceLocalRouter_basic = `
+resource sakuracloud_switch "foobar" {
+  name = "{{ .arg0 }}"
+}
+
+data sakuracloud_zone "current" {}
+
+resource "sakuracloud_local_router" "foobar" {
+  switch {
+    code     = sakuracloud_switch.foobar.id
+    category = "cloud"
+    zone_id  = data.sakuracloud_zone.current.name
+  }
+  network_interface {
+    vip          = "192.168.11.1"
+    ip_addresses = ["192.168.11.11", "192.168.11.12"]
+    netmask      = 24
+    vrid         = 1
+  }
+
+  name        = "{{ .arg0 }}"
+  description = "description"
+  tags        = ["tag1", "tag2", "tag3"]
+}
+
+data "sakuracloud_local_router" "foobar" {
+  filter {
+	names = [sakuracloud_local_router.foobar.name]
+  }
+}`
diff --git a/sakuracloud/resource_sakuracloud_local_router_test.go b/sakuracloud/resource_sakuracloud_local_router_test.go
new file mode 100644
index 00000000..0755e370
--- /dev/null
+++ b/sakuracloud/resource_sakuracloud_local_router_test.go
@@ -0,0 +1,399 @@
+// Copyright 2016-2020 terraform-provider-sakuracloud authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sakuracloud
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
+	"github.com/hashicorp/terraform-plugin-sdk/terraform"
+	"github.com/sacloud/libsacloud/v2/sacloud"
+)
+
+func TestAccSakuraCloudLocalRouter_basic(t *testing.T) {
+	resourceName := "sakuracloud_local_router.foobar"
+	rand := randomName()
+
+	var localRouter sacloud.LocalRouter
+	resource.ParallelTest(t, resource.TestCase{
+		PreCheck:  func() { testAccPreCheck(t) },
+		Providers: testAccProviders,
+		CheckDestroy: resource.ComposeTestCheckFunc(
+			testCheckSakuraCloudIconDestroy,
+			testCheckSakuraCloudLocalRouterDestroy,
+			testCheckSakuraCloudSwitchDestroy,
+		),
+		Steps: []resource.TestStep{
+			{
+				Config: buildConfigWithArgs(testAccSakuraCloudLocalRouter_basic, rand),
+				Check: resource.ComposeTestCheckFunc(
+					testCheckSakuraCloudLocalRouterExists(resourceName, &localRouter),
+					resource.TestCheckResourceAttr(resourceName, "name", rand),
+					resource.TestCheckResourceAttr(resourceName, "description", "description"),
+					resource.TestCheckResourceAttr(resourceName, "tags.#", "2"),
+					resource.TestCheckResourceAttr(resourceName, "tags.4151227546", "tag1"),
+					resource.TestCheckResourceAttr(resourceName, "tags.1852302624", "tag2"),
+					resource.TestCheckResourceAttrPair(
+						resourceName, "switch.0.code",
+						"sakuracloud_switch.foobar", "id"),
+					resource.TestCheckResourceAttr(resourceName, "switch.0.category", "cloud"),
+					resource.TestCheckResourceAttrPair(
+						resourceName, "switch.0.zone_id",
+						"data.sakuracloud_zone.current", "name"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.vip", "192.168.11.1"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.ip_addresses.#", "2"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.ip_addresses.0", "192.168.11.11"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.ip_addresses.1", "192.168.11.12"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.netmask", "24"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.vrid", "1"),
+					resource.TestCheckResourceAttr(resourceName, "static_route.#", "1"),
+					resource.TestCheckResourceAttr(resourceName, "static_route.0.prefix", "10.0.0.0/24"),
+					resource.TestCheckResourceAttr(resourceName, "static_route.0.next_hop", "192.168.11.2"),
+					resource.TestCheckResourceAttrPair(
+						resourceName, "icon_id",
+						"sakuracloud_icon.foobar", "id",
+					),
+				),
+			},
+			{
+				Config: buildConfigWithArgs(testAccSakuraCloudLocalRouter_update, rand),
+				Check: resource.ComposeTestCheckFunc(
+					testCheckSakuraCloudLocalRouterExists(resourceName, &localRouter),
+					resource.TestCheckResourceAttr(resourceName, "name", rand+"-upd"),
+					resource.TestCheckResourceAttr(resourceName, "description", "description-upd"),
+					resource.TestCheckResourceAttr(resourceName, "tags.#", "2"),
+					resource.TestCheckResourceAttr(resourceName, "tags.2362157161", "tag1-upd"),
+					resource.TestCheckResourceAttr(resourceName, "tags.3412841145", "tag2-upd"),
+					resource.TestCheckResourceAttrPair(
+						resourceName, "switch.0.code",
+						"sakuracloud_switch.foobar", "id"),
+					resource.TestCheckResourceAttr(resourceName, "switch.0.category", "cloud"),
+					resource.TestCheckResourceAttrPair(
+						resourceName, "switch.0.zone_id",
+						"data.sakuracloud_zone.current", "name"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.vip", "192.168.21.1"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.ip_addresses.#", "2"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.ip_addresses.0", "192.168.21.11"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.ip_addresses.1", "192.168.21.12"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.netmask", "24"),
+					resource.TestCheckResourceAttr(resourceName, "network_interface.0.vrid", "2"),
+					resource.TestCheckResourceAttr(resourceName, "static_route.#", "2"),
+					resource.TestCheckResourceAttr(resourceName, "static_route.0.prefix", "10.0.0.0/24"),
+					resource.TestCheckResourceAttr(resourceName, "static_route.0.next_hop", "192.168.21.2"),
+					resource.TestCheckResourceAttr(resourceName, "static_route.1.prefix", "172.16.0.0/16"),
+					resource.TestCheckResourceAttr(resourceName, "static_route.1.next_hop", "192.168.21.3"),
+					resource.TestCheckResourceAttr(resourceName, "icon_id", ""),
+				),
+			},
+		},
+	})
+}
+
+func testCheckSakuraCloudLocalRouterExists(n string, loadBalancer *sacloud.LocalRouter) resource.TestCheckFunc {
+	return func(s *terraform.State) error {
+		rs, ok := s.RootModule().Resources[n]
+
+		if !ok {
+			return fmt.Errorf("not found: %s", n)
+		}
+
+		if rs.Primary.ID == "" {
+			return errors.New("no LocalRouter ID is set")
+		}
+
+		client := testAccProvider.Meta().(*APIClient)
+		lrOp := sacloud.NewLocalRouterOp(client)
+
+		foundLocalRouter, err := lrOp.Read(context.Background(), sakuraCloudID(rs.Primary.ID))
+		if err != nil {
+			return err
+		}
+
+		if foundLocalRouter.ID.String() != rs.Primary.ID {
+			return fmt.Errorf("not found LocalRouter: %s", rs.Primary.ID)
+		}
+
+		*loadBalancer = *foundLocalRouter
+		return nil
+	}
+}
+
+func testCheckSakuraCloudLocalRouterDestroy(s *terraform.State) error {
+	client := testAccProvider.Meta().(*APIClient)
+	lrOp := sacloud.NewLocalRouterOp(client)
+
+	for _, rs := range s.RootModule().Resources {
+		if rs.Type != "sakuracloud_local_router" {
+			continue
+		}
+		if rs.Primary.ID == "" {
+			continue
+		}
+
+		_, err := lrOp.Read(context.Background(), sakuraCloudID(rs.Primary.ID))
+		if err == nil {
+			return fmt.Errorf("still exists LocalRouter: %s", rs.Primary.ID)
+		}
+	}
+
+	return nil
+}
+
+func TestAccImportSakuraCloudLocalRouter_basic(t *testing.T) {
+	rand := randomName()
+
+	checkFn := func(s []*terraform.InstanceState) error {
+		if len(s) != 1 {
+			return fmt.Errorf("expected 1 state: %#v", s)
+		}
+		expects := map[string]string{
+			"name":                               rand,
+			"description":                        "description",
+			"tags.4151227546":                    "tag1",
+			"tags.1852302624":                    "tag2",
+			"switch.0.category":                  "cloud",
+			"switch.0.zone_id":                   os.Getenv("SAKURACLOUD_ZONE"),
+			"network_interface.0.vip":            "192.168.11.1",
+			"network_interface.0.ip_addresses.0": "192.168.11.11",
+			"network_interface.0.ip_addresses.1": "192.168.11.12",
+			"network_interface.0.netmask":        "24",
+			"network_interface.0.vrid":           "1",
+			"static_route.0.prefix":              "10.0.0.0/24",
+			"static_route.0.next_hop":            "192.168.11.2",
+		}
+
+		if err := compareStateMulti(s[0], expects); err != nil {
+			return err
+		}
+		return stateNotEmptyMulti(s[0], "icon_id", "switch.0.code")
+	}
+
+	resourceName := "sakuracloud_local_router.foobar"
+
+	resource.ParallelTest(t, resource.TestCase{
+		PreCheck:     func() { testAccPreCheck(t) },
+		Providers:    testAccProviders,
+		CheckDestroy: testCheckSakuraCloudLocalRouterDestroy,
+		Steps: []resource.TestStep{
+			{
+				Config: buildConfigWithArgs(testAccSakuraCloudLocalRouter_basic, rand),
+			},
+			{
+				ResourceName:      resourceName,
+				ImportState:       true,
+				ImportStateCheck:  checkFn,
+				ImportStateVerify: true,
+			},
+		},
+	})
+}
+
+const testAccSakuraCloudLocalRouter_basic = `
+resource "sakuracloud_switch" "foobar" {
+  name = "{{ .arg0 }}"
+}
+
+data sakuracloud_zone "current" {}
+
+resource "sakuracloud_local_router" "foobar" {
+  switch {
+    code     = sakuracloud_switch.foobar.id
+    category = "cloud"
+    zone_id  = data.sakuracloud_zone.current.name
+  }
+  network_interface {
+    vip          = "192.168.11.1"
+    ip_addresses = ["192.168.11.11", "192.168.11.12"]
+    netmask      = 24
+    vrid         = 1
+  }
+  static_route {
+    prefix   = "10.0.0.0/24"
+    next_hop = "192.168.11.2"
+  }
+
+  name        = "{{ .arg0 }}"
+  description = "description"
+  tags        = ["tag1", "tag2"]
+  icon_id     = sakuracloud_icon.foobar.id
+}
+
+resource "sakuracloud_icon" "foobar" {
+  name          = "{{ .arg0 }}"
+  base64content = "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAIAAADYYG7QAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAACdBJREFUWMPNmHtw1NUVx8+5v9/+9rfJPpJNNslisgmIiCCgDQZR5GWnilUDPlpUqjOB2mp4qGM7tVOn/yCWh4AOVUprHRVB2+lMa0l88Kq10iYpNYPWkdeAmFjyEJPN7v5+v83ec/rH3Q1J2A2Z1hnYvz755ZzzvXPPveeee/GbC24FJmZGIYD5QgPpTBIAAICJLgJAwUQMAIDMfOEBUQchgJmAEC8CINLPThpfFCAG5orhogCBQiAAEyF8PQCATEQyxQzMzFIi4Ojdv86UEVF/f38ymezv7yciANR0zXAZhuHSdR0RRxNHZyJEBERmQvhfAAABIJlMJhIJt9t9TXX11GlTffleQGhvbz/4YeuRw4c13ZWfnycQR9ACQEShAyIxAxEKMXoAIVQ6VCzHcSzLmj937qqVK8aNrYKhv4bGxue3bvu8rc3n9+ualisyMzOltMjYccBqWanKdD5gBgAppZNMJhKJvlgs1heLxWL3fPfutU8/VVhYoGx7e3uJyOVyAcCEyy6bN2d266FDbW3thsuFI0gA4qy589PTOJC7EYEBbNu2ElYg4J9e/Y3p1dWBgN+l67csWKBC/mrbth07dnafOSMQp0y58pEVK2tm1ABAW9vn93zvgYRl5+XlAXMuCbxh3o3MDMyIguE8wADRaJ/H7Vp873119y8JBALDsrN8xcpXX3utoKDQNE1iiEV7ieSzmzYuXrwYAH7z4m83bNocDAZ1Tc8hQThrzjwYxY8BmCjaF/P78n+xZs0Ns64f+Ndnn53yevOLioo2btq8bsOGsvAYn9eHAoFZStnR0aFpWsObfxw/fvzp06fvXnyvZVmmx4M5hHQa3S4DwIRlm4Zr7dNPz7r+OgDo6el5bsuWtxrf6u7u9njygsHC9i/+U1Ia9ubnMzATA7MQIlRS8tnJk3/e1fDoI6vKysoqK8pbP/q323RDdi2hq/0ysHGyAwopU4lEfNXKlWo0Hx069MDSZcePHy8MBk3Tk0ylTnd1+wsKTNMERLUGlLtA1A3jyNEjagIKgsFk0gEM5NCSOst0+wEjAEvHtktKSuoeWAIAX3311f11Szs7OydcPtFwGYDp0sagWhoa7K4G5/f71TfHskEVdHXMn6M16CzLDcRkWfaM6dWm6QGAjZs2t7W1X1JeYRgGMzERMxOnNYa5O8mkrmkzr50JAKlUqq29Le2VQ0sACmYmIvU1OwAmLKt6ejUAyJTcu3dfQTCoaZqUkgEoY0ODvKRMSWbLsjo6O2fPmbuw9nYAOHjw4KdHjhqGoRqgLFpS6oNOE84JRDLVX1FeDgBd3V0pIrfLxZn5GGLMrE40y7YTCcula7W3167++c+UzfNbtzGRK+ObxR1RZyJARPUpNxBzPBYDAE3ThCYkETMjIPMQdwCwbNttGItqb6uqrJo2deqMGTVK8qWXX969+92SsjAi5hRF1BkQKJ3REUDXtE+PHL3ppptCoVBpcXFXVzdJqerFWWNmKaVt2T9YWldf//Dg6rL52efWrV/vCxQYLhdJmV2LmaUUkEkZZGbvXGBm0+P563vvqT/vW7LEcRwnmUxv7wFjZiYyDJdabQCQSsnt27d/6+YFT61Z4/UHBvZadi1mQBRERMwEMAIwkdttNh/8V2trKwB85647a2tv7+npTfb3y6HGKLREIvHKK6+my66ubd/x+p69+0KlZf5AQKV+BC0G0MaURwZGlxMAiam9vf3YsWNL7rsXAL694Oa2tvZPPvnEZRiozBABAIE1XfvggwMfffzxnXcsAoBrZ8zYs3+/pmm6ECNJIKrto4UvueQ8pxiRZduxWKympuauRQsnT56saRoAlIRCbzbsYmYhxGB7TdPcHk9LS3O4LHz1VVcFg8HmpubjJ0643W44/w8FS6kqW1YgKROW5VjWivr6P/3h93V1dYZhKNeD/2zp7elVjfAQLyKP2+0PFG5/NZ242XNm25bNRCNrKUjfy5gIzwXE/mQyEYs98dMnHnrw+yr6hx+2/qOp6djRo43vvGu4XJquZ3X3mO7OL8+cOnUqEolURSpUx53LeDDolDlE+ByQRNG+vlmzZ6vROI69fMWqN954Ix5PBAoLC4PBfK+XMqfSEHdEQJRS2ratyl1KSmLG3FoDoKcXFCIQDQOZTCLAQ8uWKtNlD/5w546dkaqqKq8XERDFQIkb7g6QSqUK/f5wOAwA0WgUiM+u/WxaChBRJxSgzsXhK5+sZDISiVxTUwMAjY2Nu3Y1RMZd6vXmAzCAIOB0uHP2SyqVisViCxcu9Pl8ANDc0oK6xswkxMg7mon0dGHMUqkg6Tjh0lLTdAPABwf+niKZ5zFRtRmQ8RrqyACyv783Gi0vL390eb0qqm+/szvPNNMzNGIFRnUvA0SAzOwNAiLJmU4zHo8DCgAgZgAETtswyX4pk8lkehP0pywrUTV27JaNGyqrKgHgha1bT548WRYOMwDk1hrIna46gbTAUBBCUwcqAFw6frwuRCqV0nUdmFB1MCRtx9E0bWwkEresRDzu9/nm3Th/Vf3DoVAIAJqbmtauXZfv9WpCpBd7Dq00EOGkKdNylCi0EgkhxP4971ZUVJw8ceK2RXd0dX9ZUFCgCaFyYTtOrC/22CMrf/LjH3V0dvX1RSsjEVemUDU3NS1d9uAXHR2lpaVqV4+iMIJWXFKKiEpgCCAKxI6OjuLioutmziwoLBxTFn7r7Xei0WhKSsdxYvF4PJ649Zabn1m/DhC93vxgMKiKuGUlntm46bHHHz/T0xsqKdEEZpYKZ9caJIpXTJmWfuVDofpPBcAMKKLRXoHwl727x106HgAOHDiw5ZcvHD5ymBiCwcJFtbXLM21GQ0ODZVm90ej77/9t3779XV2dBcEifyCgIcLQyCMBMU6cNCX3wQIkqbOzY+LlE373+s6KSER97untdSy7tKx0wHD16tVPPvkkAIDQvV6fz+fNz/emXzyAYVS5yqSsqLh4UM8GwwAFmqZ54sSJXY2NJSUlkyZNAgDTNL1er/Jvb29/uL7+1y++VFQcKg2PCYVCfr/XND1C01QnnytydkDECVdcqdpqtXGGgcqulHTmy+54PH71VdNunD+/sqoSEaPRaEtzy569exO2UxQM5nm9ynpQgrIEPA8w42UTJ6dLEkNWUI0KMTu2E4v3xftiSccGAKHpnrw8v8/vyfPoug4Zv1xxRgOIoDNJQAEMmfo9HNT9DxFN03QbRrCwCNQjHAp1gVc2mQKbM86oAFCA0GDQnSEXqMcGwPQjmND1zGgEAFBmNOeNMzIQSZ0GXvJHuJedPXRkLhiN+2hAVxUdz77yXWDQUdMGFUa40DC4Y/ya5vz/BMEkmVm9dl94QPwvNJB+oilXgHEAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTYtMDItMTBUMjE6MDg6MzMtMDg6MDB4P0OtAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE2LTAyLTEwVDIxOjA4OjMzLTA4OjAwCWL7EQAAAABJRU5ErkJggg=="
+}
+`
+
+const testAccSakuraCloudLocalRouter_update = `
+resource "sakuracloud_switch" "foobar" {
+  name = "{{ .arg0 }}"
+}
+
+data sakuracloud_zone "current" {}
+
+resource "sakuracloud_local_router" "foobar" {
+  switch {
+    code     = sakuracloud_switch.foobar.id
+    category = "cloud"
+    zone_id  = data.sakuracloud_zone.current.name
+  }
+  network_interface {
+    vip          = "192.168.21.1"
+    ip_addresses = ["192.168.21.11", "192.168.21.12"]
+    netmask      = 24
+    vrid         = 2
+  }
+  static_route {
+    prefix   = "10.0.0.0/24"
+    next_hop = "192.168.21.2"
+  }
+  static_route {
+    prefix   = "172.16.0.0/16"
+    next_hop = "192.168.21.3"
+  }
+
+  name        = "{{ .arg0 }}-upd"
+  description = "description-upd"
+  tags        = ["tag1-upd", "tag2-upd"]
+}
+`
+
+func TestAccSakuraCloudLocalRouter_peering(t *testing.T) {
+	resourceName1 := "sakuracloud_local_router.foobar1"
+	resourceName2 := "sakuracloud_local_router.foobar2"
+	rand := randomName()
+
+	resource.ParallelTest(t, resource.TestCase{
+		PreCheck:  func() { testAccPreCheck(t) },
+		Providers: testAccProviders,
+		CheckDestroy: resource.ComposeTestCheckFunc(
+			testCheckSakuraCloudIconDestroy,
+			testCheckSakuraCloudLocalRouterDestroy,
+			testCheckSakuraCloudSwitchDestroy,
+		),
+		Steps: []resource.TestStep{
+			{
+				Config: buildConfigWithArgs(testAccSakuraCloudLocalRouter_peering, rand),
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttrPair(resourceName1, "id", resourceName2, "peer.0.peer_id"),
+					resource.TestCheckResourceAttrPair(resourceName1, "secret_keys.0", resourceName2, "peer.0.secret_key"),
+					resource.TestCheckResourceAttr(resourceName2, "peer.0.description", "description"),
+				),
+			},
+			{
+				Config: buildConfigWithArgs(testAccSakuraCloudLocalRouter_peeringDisconnect, rand),
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttr(resourceName1, "peer.#", "0"),
+					resource.TestCheckResourceAttr(resourceName2, "peer.#", "0"),
+				),
+			},
+		},
+	})
+}
+
+const testAccSakuraCloudLocalRouter_peering = `
+resource "sakuracloud_switch" "foobar1" {
+  name = "{{ .arg0 }}"
+}
+resource "sakuracloud_switch" "foobar2" {
+  name = "{{ .arg0 }}"
+}
+
+data sakuracloud_zone "current" {}
+
+resource "sakuracloud_local_router" "foobar1" {
+  switch {
+    code     = sakuracloud_switch.foobar1.id
+    category = "cloud"
+    zone_id  = data.sakuracloud_zone.current.name
+  }
+  network_interface {
+    vip          = "192.168.11.1"
+    ip_addresses = ["192.168.11.11", "192.168.11.12"]
+    netmask      = 24
+    vrid         = 1
+  }
+
+  name        = "{{ .arg0 }}"
+}
+
+resource "sakuracloud_local_router" "foobar2" {
+  switch {
+    code     = sakuracloud_switch.foobar2.id
+    category = "cloud"
+    zone_id  = data.sakuracloud_zone.current.name
+  }
+  network_interface {
+    vip          = "192.168.12.1"
+    ip_addresses = ["192.168.12.11", "192.168.12.12"]
+    netmask      = 24
+    vrid         = 1
+  }
+  peer {
+    peer_id     = sakuracloud_local_router.foobar1.id
+    secret_key  = sakuracloud_local_router.foobar1.secret_keys.0
+    description = "description"
+  }
+
+  name        = "{{ .arg0 }}"
+}
+`
+
+const testAccSakuraCloudLocalRouter_peeringDisconnect = `
+resource "sakuracloud_switch" "foobar1" {
+  name = "{{ .arg0 }}"
+}
+resource "sakuracloud_switch" "foobar2" {
+  name = "{{ .arg0 }}"
+}
+
+data sakuracloud_zone "current" {}
+
+resource "sakuracloud_local_router" "foobar1" {
+  switch {
+    code     = sakuracloud_switch.foobar1.id
+    category = "cloud"
+    zone_id  = data.sakuracloud_zone.current.name
+  }
+  network_interface {
+    vip          = "192.168.11.1"
+    ip_addresses = ["192.168.11.11", "192.168.11.12"]
+    netmask      = 24
+    vrid         = 1
+  }
+
+  name        = "{{ .arg0 }}"
+}
+
+resource "sakuracloud_local_router" "foobar2" {
+  switch {
+    code     = sakuracloud_switch.foobar2.id
+    category = "cloud"
+    zone_id  = data.sakuracloud_zone.current.name
+  }
+  network_interface {
+    vip          = "192.168.12.1"
+    ip_addresses = ["192.168.12.11", "192.168.12.12"]
+    netmask      = 24
+    vrid         = 1
+  }
+
+  name        = "{{ .arg0 }}"
+}
+`