forked from mrkvm/rados.go
/
rados.go
201 lines (165 loc) · 5.42 KB
/
rados.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// Package rados provides Go bindings for the CEPH RADOS client library (librados)
// We attempt to adhere to the style of the Go OS package as much as possible
// (for example, our Object type implements the FileStat and ReaderAt/WriterAt
// interfaces).
package rados
/*
#cgo LDFLAGS: -lrados
#include "stdlib.h"
#include "rados/librados.h"
*/
import "C"
import (
"bytes"
"unsafe"
)
// Rados provides a handle for interacting with a RADOS cluster.
type Rados struct {
rados C.rados_t
size uint64
used uint64
avail uint64
nObjects uint64
}
// New returns a RADOS cluster handle that is used to create IO
// Contexts and perform other RADOS actions. If configFile is
// non-empty, RADOS will look for its configuration there, otherwise
// the default paths will be searched (e.g., /etc/ceph/ceph.conf).
//
// TODO: allow caller to specify Ceph user.
func New(configFile string) (*Rados, error) {
r := &Rados{}
var cerr C.int
if cerr = C.rados_create(&r.rados, nil); cerr < 0 {
return nil, radosReturnCodeError(cerr)
}
if configFile == "" {
cerr = C.rados_conf_read_file(r.rados, nil)
} else {
cconfigFile := C.CString(configFile)
defer C.free(unsafe.Pointer(cconfigFile))
cerr = C.rados_conf_read_file(r.rados, cconfigFile)
}
if cerr < 0 {
return nil, radosReturnCodeError(cerr)
}
if cerr = C.rados_connect(r.rados); cerr < 0 {
return nil, radosReturnCodeError(cerr)
}
// Fill in cluster statistics
if err := r.Stat(); err != nil {
r.Release()
return nil, err
}
return r, nil
}
// NewDefault returns a RADOS cluster handle based on the default config file.
// See New() for more information.
func NewDefault() (r *Rados, err error) {
r, err = New("")
return r, err
}
// Stat retrieves the current cluster statistics and stores them in
// the Rados structure.
func (r *Rados) Stat() error {
var cstat C.struct_rados_cluster_stat_t
if cerr := C.rados_cluster_stat(r.rados, &cstat); cerr < 0 {
return radosReturnCodeError(cerr)
}
r.size = uint64(cstat.kb)
r.used = uint64(cstat.kb_used)
r.avail = uint64(cstat.kb_avail)
r.nObjects = uint64(cstat.num_objects)
return nil
}
// Size returns the total size of the cluster in kilobytes.
func (r *Rados) Size() uint64 {
return r.size
}
// Used returns the number of used kilobytes in the cluster.
func (r *Rados) Used() uint64 {
return r.used
}
// Avail returns the number of available kilobytes in the cluster.
func (r *Rados) Avail() uint64 {
return r.avail
}
// NObjects returns the number of objects in the cluster.
func (r *Rados) NObjects() uint64 {
return r.nObjects
}
// Release handle and disconnect from RADOS cluster.
//
// TODO: track all open ioctx, ensure all async operations have
// completed before calling rados_shutdown, because it doesn't do that
// itself.
func (r *Rados) Release() error {
C.rados_shutdown(r.rados)
return nil
}
// CreatePool creates the named pool in the given RADOS cluster.
// CreatePool uses the default admin user and crush rule.
//
// TODO: Add ability to create pools with specific admin users/crush rules.
func (r *Rados) CreatePool(poolName string) error {
cname := C.CString(poolName)
defer C.free(unsafe.Pointer(cname))
if cerr := C.rados_pool_create(r.rados, cname); cerr < 0 {
return radosReturnCodeError(cerr)
}
return nil
}
// DeletePool deletes the named pool in the given RADOS cluster.
func (r *Rados) DeletePool(poolName string) error {
cname := C.CString(poolName)
defer C.free(unsafe.Pointer(cname))
if cerr := C.rados_pool_delete(r.rados, cname); cerr < 0 {
return radosReturnCodeError(cerr)
}
return nil
}
// ListPools retuns a list of pools in the given RADOS cluster as
// a slice of strings.
func (r *Rados) ListPools() ([]string, error) {
var buf []byte
bufSize := 256 // Initial guess at amount of space we need
// Get the list of pools from RADOS. Note we may need to
// retry if our initial buffer size isn't big enough to
// hold all pools.
for {
buf = make([]byte, bufSize)
cdata, cdatalen := byteSliceToBuffer(buf)
// rados_pool_list() returns the number of bytes needed
// to return all pools.
cbufsize := C.rados_pool_list(r.rados, cdata, cdatalen)
if cbufsize < 0 {
return nil, radosReturnCodeError(cbufsize)
} else if int(cbufsize) > bufSize {
// We didn't have enough space -- try again
bufSize = int(cbufsize)
continue
}
break
}
// rados_pool_list() returns an array strings separated by NULL bytes
// with two NULL bytes at the end. Break these into individual strings.
pools := make([]string, 0)
poolsBuf := bytes.Split(buf, []byte{0})
for i, _ := range poolsBuf {
if len(poolsBuf[i]) == 0 {
continue
}
pools = append(pools, string(poolsBuf[i]))
}
return pools, nil
}
// byteSliceToBuffer is a utility function to convert the given byte slice
// to a C character pointer. It returns the pointer and the size of
// the data (as a C size_t).
func byteSliceToBuffer(data []byte) (*C.char, C.size_t) {
if len(data) > 0 {
return (*C.char)(unsafe.Pointer(&data[0])), C.size_t(len(data))
} else {
return (*C.char)(unsafe.Pointer(&data)), C.size_t(0)
}
}