github.com/yasker/longhorn-engine@v0.0.0-20160621014712-6ed6cfca0729/backend/remote/remote.go (about) 1 package remote 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "net" 10 "net/http" 11 "strconv" 12 "time" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/rancher/longhorn/replica/rest" 16 "github.com/rancher/longhorn/rpc" 17 "github.com/rancher/longhorn/types" 18 "github.com/rancher/longhorn/util" 19 journal "github.com/rancher/sparse-tools/stats" 20 ) 21 22 var ( 23 pingRetries = 6 24 pingTimeout = 20 * time.Second 25 pingInveral = 2 * time.Second 26 27 timeout = 30 * time.Second 28 requestBuffer = 1024 29 ErrPingTimeout = errors.New("Ping timeout") 30 ) 31 32 func New() types.BackendFactory { 33 return &Factory{} 34 } 35 36 type Factory struct { 37 } 38 39 type Remote struct { 40 types.ReaderWriterAt 41 name string 42 pingURL string 43 replicaURL string 44 httpClient *http.Client 45 closeChan chan struct{} 46 } 47 48 func (r *Remote) Close() error { 49 r.closeChan <- struct{}{} 50 logrus.Infof("Closing: %s", r.name) 51 return r.doAction("close", "") 52 } 53 54 func (r *Remote) open() error { 55 logrus.Infof("Opening: %s", r.name) 56 return r.doAction("open", "") 57 } 58 59 func (r *Remote) Snapshot(name string) error { 60 logrus.Infof("Snapshot: %s %s", r.name, name) 61 return r.doAction("snapshot", name) 62 } 63 64 func (r *Remote) doAction(action, name string) error { 65 body := io.Reader(nil) 66 if name != "" { 67 buffer := &bytes.Buffer{} 68 if err := json.NewEncoder(buffer).Encode(&map[string]string{"name": name}); err != nil { 69 return err 70 } 71 body = buffer 72 } 73 74 req, err := http.NewRequest("POST", r.replicaURL+"?action="+action, body) 75 if err != nil { 76 return err 77 } 78 79 if name != "" { 80 req.Header.Add("Content-Type", "application/json") 81 } 82 83 resp, err := r.httpClient.Do(req) 84 if err != nil { 85 return err 86 } 87 defer resp.Body.Close() 88 89 if resp.StatusCode != http.StatusOK { 90 return fmt.Errorf("Bad status: %d %s", resp.StatusCode, resp.Status) 91 } 92 93 return nil 94 } 95 96 func (r *Remote) Size() (int64, error) { 97 replica, err := r.info() 98 if err != nil { 99 return 0, err 100 } 101 return strconv.ParseInt(replica.Size, 10, 0) 102 } 103 104 func (r *Remote) SectorSize() (int64, error) { 105 replica, err := r.info() 106 if err != nil { 107 return 0, err 108 } 109 return replica.SectorSize, nil 110 } 111 112 func (r *Remote) info() (rest.Replica, error) { 113 var replica rest.Replica 114 req, err := http.NewRequest("GET", r.replicaURL, nil) 115 if err != nil { 116 return replica, err 117 } 118 119 resp, err := r.httpClient.Do(req) 120 if err != nil { 121 return replica, err 122 } 123 defer resp.Body.Close() 124 125 if resp.StatusCode != http.StatusOK { 126 return replica, fmt.Errorf("Bad status: %d %s", resp.StatusCode, resp.Status) 127 } 128 129 err = json.NewDecoder(resp.Body).Decode(&replica) 130 return replica, err 131 } 132 133 func (rf *Factory) Create(address string) (types.Backend, error) { 134 logrus.Infof("Connecting to remote: %s", address) 135 136 controlAddress, dataAddress, _, err := util.ParseAddresses(address) 137 if err != nil { 138 return nil, err 139 } 140 141 r := &Remote{ 142 name: address, 143 replicaURL: fmt.Sprintf("http://%s/v1/replicas/1", controlAddress), 144 pingURL: fmt.Sprintf("http://%s/ping", controlAddress), 145 httpClient: &http.Client{ 146 Timeout: timeout, 147 }, 148 closeChan: make(chan struct{}, 1), 149 } 150 151 replica, err := r.info() 152 if err != nil { 153 return nil, err 154 } 155 156 if replica.State != "closed" { 157 return nil, fmt.Errorf("Replica must be closed, Can not add in state: %s", replica.State) 158 } 159 160 conn, err := net.Dial("tcp", dataAddress) 161 if err != nil { 162 return nil, err 163 } 164 165 rpc := rpc.NewClient(conn) 166 r.ReaderWriterAt = rpc 167 168 if err := r.open(); err != nil { 169 return nil, err 170 } 171 172 go r.monitorPing(rpc) 173 174 return r, nil 175 } 176 177 func (r *Remote) monitorPing(client *rpc.Client) error { 178 ticker := time.NewTicker(pingInveral) 179 defer ticker.Stop() 180 181 retry := 0 182 for { 183 select { 184 case <-r.closeChan: 185 return nil 186 case <-ticker.C: 187 if err := r.Ping(client); err == nil { 188 retry = 0 // reset on success 189 } else { 190 if retry < pingRetries { 191 retry++ 192 logrus.Errorf("Ping retry %v on replica %v; error: %v", retry, r.replicaURL, err) 193 journal.PrintLimited(1000) //flush automatically upon retry 194 } else { 195 logrus.Errorf("Failed to get ping response from replica %v; error: %v", r.replicaURL, err) 196 journal.PrintLimited(1000) //flush automatically upon error 197 client.SetError(err) 198 return err 199 } 200 } 201 } 202 } 203 } 204 205 func (r *Remote) Ping(client *rpc.Client) error { 206 ret := make(chan error, 1) 207 // use replica data addr for ping target tracking 208 opID := journal.InsertPendingOp(time.Now(), client.TargetID(), journal.OpPing, 0) 209 210 go func() { 211 resp, err := r.httpClient.Get(r.pingURL) 212 if err != nil { 213 ret <- err 214 return 215 } 216 defer resp.Body.Close() 217 if resp.StatusCode != 200 { 218 ret <- fmt.Errorf("Non-200 response %d from ping to %s", resp.StatusCode, r.name) 219 return 220 } 221 ret <- nil 222 }() 223 224 select { 225 case err := <-ret: 226 journal.RemovePendingOp(opID, err == nil) 227 return err 228 case <-time.After(pingTimeout): 229 journal.RemovePendingOp(opID, false) 230 return ErrPingTimeout 231 } 232 }