diff --git a/api/instance_service.go b/api/instance_service.go index d5be31d8..bcf5a965 100644 --- a/api/instance_service.go +++ b/api/instance_service.go @@ -105,6 +105,7 @@ func (s *InstanceAPI) Start(ctx context.Context, in *StartRequest) (*StartReply, "instance_id": in.GetInstanceId(), "state": lastState.String(), }) + switch lastState.GetState() { case model.InstanceState_REGISTERED: if err := lastState.ValidateGoalState(model.InstanceState_QUEUED); err != nil { @@ -112,6 +113,13 @@ func (s *InstanceAPI) Start(ctx context.Context, in *StartRequest) (*StartReply, // TODO: Investigate gRPC error response return nil, err } + + err := s.api.instanceScheduler.Assign(inst) + if err != nil { + flog.Error(err) + return nil, err + } + if err := model.Instances(ctx).UpdateState(in.GetInstanceId(), model.InstanceState_QUEUED); err != nil { flog.Error(err) return nil, err diff --git a/api/server.go b/api/server.go index 25ddc044..f555422a 100644 --- a/api/server.go +++ b/api/server.go @@ -7,6 +7,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/axsh/openvdc/model" "github.com/axsh/openvdc/model/backend" + "github.com/axsh/openvdc/scheduler" "github.com/gogo/protobuf/proto" mesos "github.com/mesos/mesos-go/mesosproto" sched "github.com/mesos/mesos-go/scheduler" @@ -18,11 +19,12 @@ import ( //go:generate protoc -I../proto -I${GOPATH}/src --go_out=plugins=grpc:${GOPATH}/src ../proto/v1.proto type APIServer struct { - server *grpc.Server - modelStoreAddr backend.ConnectionAddress - scheduler sched.SchedulerDriver - mesosMasterAddr *mesos.Address - mMesosMasterAddr sync.Mutex + server *grpc.Server + modelStoreAddr backend.ConnectionAddress + scheduler sched.SchedulerDriver + mesosMasterAddr *mesos.Address + mMesosMasterAddr sync.Mutex + instanceScheduler scheduler.Schedule } type serverStreamWithContext struct { @@ -34,7 +36,7 @@ func (ss *serverStreamWithContext) Context() context.Context { return ss.ctx } -func NewAPIServer(modelAddr backend.ConnectionAddress, driver sched.SchedulerDriver, ctx context.Context) *APIServer { +func NewAPIServer(modelAddr backend.ConnectionAddress, driver sched.SchedulerDriver, instanceScheduler scheduler.Schedule, ctx context.Context) *APIServer { // Assert the ctx has "cluster.backend" key model.GetClusterBackendCtx(ctx) @@ -66,9 +68,10 @@ func NewAPIServer(modelAddr backend.ConnectionAddress, driver sched.SchedulerDri grpc.StreamInterceptor(insertFullMethodStream), } s := &APIServer{ - server: grpc.NewServer(sopts...), - modelStoreAddr: modelAddr, - scheduler: driver, + server: grpc.NewServer(sopts...), + modelStoreAddr: modelAddr, + scheduler: driver, + instanceScheduler: instanceScheduler, } RegisterInstanceServer(s.server, &InstanceAPI{api: s}) diff --git a/api/server_test.go b/api/server_test.go index c534a4c4..ebe1bd27 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -8,14 +8,18 @@ import ( "github.com/axsh/openvdc/internal/unittest" "github.com/axsh/openvdc/model" "github.com/axsh/openvdc/model/backend" + "github.com/axsh/openvdc/scheduler" "golang.org/x/net/context" ) func TestNewAPIServer(t *testing.T) { ze := &backend.ZkEndpoint{} ze.Set(unittest.TestZkServer) + + sched := scheduler.Schedule{} + // TODO: Set mock SchedulerDriver - s := NewAPIServer(ze, nil, model.WithMockClusterBackendCtx(context.Background())) + s := NewAPIServer(ze, nil, sched, model.WithMockClusterBackendCtx(context.Background())) if s == nil { t.Error("NewAPIServer() returned nil") } @@ -28,8 +32,11 @@ func TestAPIServerRun(t *testing.T) { } ze := &backend.ZkEndpoint{} ze.Set(unittest.TestZkServer) + + sched := scheduler.Schedule{} + // TODO: Set mock SchedulerDriver - s := NewAPIServer(ze, nil, model.WithMockClusterBackendCtx(context.Background())) + s := NewAPIServer(ze, nil, sched, model.WithMockClusterBackendCtx(context.Background())) go func() { time.Sleep(2 * time.Second) s.Stop() diff --git a/ci/citest/acceptance-test/tests/openvdc-acceptance-test b/ci/citest/acceptance-test/tests/openvdc-acceptance-test new file mode 100755 index 00000000..e17e3027 Binary files /dev/null and b/ci/citest/acceptance-test/tests/openvdc-acceptance-test differ diff --git a/ci/citest/acceptance-test/tests/storable_scheduler_test.go b/ci/citest/acceptance-test/tests/storable_scheduler_test.go new file mode 100644 index 00000000..0ae95389 --- /dev/null +++ b/ci/citest/acceptance-test/tests/storable_scheduler_test.go @@ -0,0 +1,14 @@ +// +build acceptance + +package tests + +import ( + "testing" +) + +func TestStorable_scheduler_LxcHugeTemplate(t *testing.T) { + _, stderr := RunCmdAndExpectFail(t, "openvdc", "run", "centos/7/lxc_huge") + if stderr == nil { + t.Error("There is no machine can satisfy resource requirement but work") + } +} diff --git a/cmd/openvdc-scheduler/main.go b/cmd/openvdc-scheduler/main.go index 7cbe68c4..90904e27 100644 --- a/cmd/openvdc-scheduler/main.go +++ b/cmd/openvdc-scheduler/main.go @@ -158,7 +158,10 @@ func execute(cmd *cobra.Command, args []string) { log.Fatalln("Faild to bind address for gRPC API: ", viper.GetString("api.endpoint")) } log.Info("Listening gRPC API on: ", viper.GetString("api.endpoint")) - grpcServer := api.NewAPIServer(zkAddr, mesosDriver, ctx) + + instanceScheduler := scheduler.Schedule{} + + grpcServer := api.NewAPIServer(zkAddr, mesosDriver, instanceScheduler, ctx) if err := detector.Detect(grpcServer); err != nil { log.WithError(err).Fatal("Failed to start mesos detector") diff --git a/handlers/types.go b/handlers/types.go index 0b5e413c..fee1e904 100644 --- a/handlers/types.go +++ b/handlers/types.go @@ -4,9 +4,8 @@ import ( "encoding/json" "fmt" "io" - "strings" - "reflect" + "strings" "github.com/axsh/openvdc/model" ) @@ -64,3 +63,7 @@ func FindByType(name string) (p ResourceHandler, ok bool) { p, ok = resourceHandlers[name] return } + +type InstanceScheduleHandler interface { + ScheduleInstance(model.InstanceResource, *model.VDCOffer) (bool, error) // compare with offer and resrouce request. +} diff --git a/handlers/vm/esxi/scheduler.go b/handlers/vm/esxi/scheduler.go new file mode 100644 index 00000000..d5bcc9db --- /dev/null +++ b/handlers/vm/esxi/scheduler.go @@ -0,0 +1,30 @@ +package esxi + +import ( + "github.com/axsh/openvdc/handlers/vm" + "github.com/axsh/openvdc/model" + "github.com/axsh/openvdc/scheduler" +) + +func init() { + scheduler.RegisterInstanceScheduleHandler("vm/esxi", &EsxiScheduler{}) +} + +type EsxiScheduler struct { +} + +func NewScheduler() *EsxiScheduler { + return new(EsxiScheduler) +} + +func (*EsxiScheduler) ScheduleInstance(ir model.InstanceResource, offer *model.VDCOffer) (bool, error) { + cpus := ir.GetVcpu() + mem := ir.GetMemoryGb() + + offerCpus := int32(vm.GetOfferScalar(offer, "cpus")) + offerMem := int32(vm.GetOfferScalar(offer, "mem") / 1000) + if cpus < offerCpus && mem < offerMem { + return true, nil + } + return false, nil +} diff --git a/handlers/vm/lxc/lxc_test.go b/handlers/vm/lxc/lxc_test.go index dc49ee1c..c79f241c 100644 --- a/handlers/vm/lxc/lxc_test.go +++ b/handlers/vm/lxc/lxc_test.go @@ -16,7 +16,6 @@ func TestResourceName(t *testing.T) { func TestTypes(t *testing.T) { assert := assert.New(t) - assert.Implements((*handlers.ResourceHandler)(nil), &LxcHandler{}) assert.Implements((*handlers.CLIHandler)(nil), &LxcHandler{}) } diff --git a/handlers/vm/lxc/scheduler.go b/handlers/vm/lxc/scheduler.go new file mode 100644 index 00000000..99934e25 --- /dev/null +++ b/handlers/vm/lxc/scheduler.go @@ -0,0 +1,30 @@ +package lxc + +import ( + "github.com/axsh/openvdc/handlers/vm" + "github.com/axsh/openvdc/model" + "github.com/axsh/openvdc/scheduler" +) + +func init() { + scheduler.RegisterInstanceScheduleHandler("vm/lxc", &LxcScheduler{}) +} + +type LxcScheduler struct { +} + +func NewScheduler() *LxcScheduler { + return new(LxcScheduler) +} + +func (*LxcScheduler) ScheduleInstance(ir model.InstanceResource, offer *model.VDCOffer) (bool, error) { + cpus := ir.GetVcpu() + mem := ir.GetMemoryGb() + + offerCpus := int32(vm.GetOfferScalar(offer, "cpus")) + offerMem := int32(vm.GetOfferScalar(offer, "mem") / 1000) + if cpus < offerCpus && mem < offerMem { + return true, nil + } + return false, nil +} diff --git a/handlers/vm/null/null_test.go b/handlers/vm/null/null_test.go index 6a44796c..9ae5ccd7 100644 --- a/handlers/vm/null/null_test.go +++ b/handlers/vm/null/null_test.go @@ -14,6 +14,5 @@ func TestResourceName(t *testing.T) { func TestTypes(t *testing.T) { assert := assert.New(t) - assert.Implements((*handlers.ResourceHandler)(nil), &NullHandler{}) assert.Implements((*handlers.CLIHandler)(nil), &NullHandler{}) } diff --git a/handlers/vm/null/scheduler.go b/handlers/vm/null/scheduler.go new file mode 100644 index 00000000..cfa3e150 --- /dev/null +++ b/handlers/vm/null/scheduler.go @@ -0,0 +1,21 @@ +package null + +import ( + "github.com/axsh/openvdc/model" + "github.com/axsh/openvdc/scheduler" +) + +func init() { + scheduler.RegisterInstanceScheduleHandler("vm/null", &NullScheduler{}) +} + +type NullScheduler struct { +} + +func NewScheduler() *NullScheduler { + return new(NullScheduler) +} + +func (*NullScheduler) ScheduleInstance(ir model.InstanceResource, offer *model.VDCOffer) (bool, error) { + return true, nil +} diff --git a/handlers/vm/qemu/qemu_test.go b/handlers/vm/qemu/qemu_test.go index acf93138..5e3d18ab 100644 --- a/handlers/vm/qemu/qemu_test.go +++ b/handlers/vm/qemu/qemu_test.go @@ -16,7 +16,6 @@ func TestResourceName(t *testing.T) { func TestTypes(t *testing.T) { assert := assert.New(t) - assert.Implements((*handlers.ResourceHandler)(nil), &QemuHandler{}) assert.Implements((*handlers.CLIHandler)(nil), &QemuHandler{}) } diff --git a/handlers/vm/qemu/scheduler.go b/handlers/vm/qemu/scheduler.go new file mode 100644 index 00000000..e9716925 --- /dev/null +++ b/handlers/vm/qemu/scheduler.go @@ -0,0 +1,30 @@ +package qemu + +import ( + "github.com/axsh/openvdc/handlers/vm" + "github.com/axsh/openvdc/model" + "github.com/axsh/openvdc/scheduler" +) + +func init() { + scheduler.RegisterInstanceScheduleHandler("vm/qemu", &QemuScheduler{}) +} + +type QemuScheduler struct { +} + +func NewScheduler() *QemuScheduler { + return new(QemuScheduler) +} + +func (*QemuScheduler) ScheduleInstance(ir model.InstanceResource, offer *model.VDCOffer) (bool, error) { + cpus := ir.GetVcpu() + mem := ir.GetMemoryGb() + + offerCpus := int32(vm.GetOfferScalar(offer, "cpus")) + offerMem := int32(vm.GetOfferScalar(offer, "mem") / 1000) + if cpus < offerCpus && mem < offerMem { + return true, nil + } + return false, nil +} diff --git a/handlers/vm/util.go b/handlers/vm/util.go new file mode 100644 index 00000000..379c93ee --- /dev/null +++ b/handlers/vm/util.go @@ -0,0 +1,26 @@ +package vm + +import "github.com/axsh/openvdc/model" + +// Generic scheduling util functions +// TODO change to dont allow non-nullable function +func GetOfferScalar(offer *model.VDCOffer, name string) float64 { + resources := filterResources(offer.Resources, func(res model.VDCOfferResource) bool { + return res.Name == name + }) + + value := 0.0 + for _, res := range resources { + value += res.Scalar + } + return value +} + +func filterResources(resources []model.VDCOfferResource, filter func(model.VDCOfferResource) bool) (result []model.VDCOfferResource) { + for _, resource := range resources { + if filter(resource) { + result = append(result, resource) + } + } + return result +} diff --git a/model/resource_templates.go b/model/resource_templates.go index dfddf933..4e2bd10b 100644 --- a/model/resource_templates.go +++ b/model/resource_templates.go @@ -24,6 +24,7 @@ func (*NullTemplate) ResourceName() string { return "vm/null" } // InstanceResource is a marker interface for instance template structs. type InstanceResource interface { + ResourceTemplate isInstanceResourceKind() // protobuf message belongs to InstanceResource should have fields below: // int32 vcpu = xx; @@ -72,3 +73,32 @@ func IsMatchingNodeGroups(res InstanceResource, offered []string) bool { } return true } + +// define openvdc's offer +type VDCOffer struct { + SlaveID string + Resources []VDCOfferResource +} + +type VDCOfferResource struct { + Name string + Type VDCOfferValueType + Scalar float64 + Ranges []VDCOfferValueRange + Set []string + // Disk +} + +type VDCOfferValueType int32 + +const ( + VDCOfferValueScalar VDCOfferValueType = 0 + VDCOfferValueRanges VDCOfferValueType = 1 + VDCOfferValueSet VDCOfferValueType = 2 + VDCOfferValueText VDCOfferValueType = 3 +) + +type VDCOfferValueRange struct { + Begin uint64 + End uint64 +} diff --git a/scheduler/mesos.go b/scheduler/mesos.go index 48630b87..6d99cce4 100644 --- a/scheduler/mesos.go +++ b/scheduler/mesos.go @@ -38,6 +38,33 @@ type VDCScheduler struct { ctx context.Context } +func convertToOpenVDCOffer(mOffer *mesos.Offer) *model.VDCOffer { + vOffer := &model.VDCOffer{} + vOffer.SlaveID = mOffer.GetSlaveId().GetValue() + vResources := []model.VDCOfferResource{} + for _, res := range mOffer.GetResources() { + vRes := model.VDCOfferResource{} + vRes.Name = res.GetName() + vRes.Type = model.VDCOfferValueType(mesos.Value_Type_value[res.GetType().String()]) + vRes.Scalar = res.GetScalar().GetValue() + + var tmpVR []model.VDCOfferValueRange + for _, r := range res.GetRanges().GetRange() { + tmp := model.VDCOfferValueRange{ + Begin: *r.Begin, + End: *r.End, + } + tmpVR = append(tmpVR, tmp) + } + vRes.Ranges = tmpVR + + vRes.Set = res.GetSet().GetItem() + vResources = append(vResources, vRes) + } + vOffer.Resources = vResources + return vOffer +} + func newVDCScheduler(ctx context.Context, listenAddr string, zkAddr backend.ZkEndpoint) *VDCScheduler { return &VDCScheduler{ listenAddr: listenAddr, @@ -96,6 +123,8 @@ func (sched *VDCScheduler) processOffers(driver sched.SchedulerDriver, offers [] sched.CheckForCrashedNodes(offers, ctx) } + sched.storeOffers(offers) + disconnected := getDisconnectedInstances(offers, ctx, driver) if len(disconnected) > 0 { @@ -187,10 +216,16 @@ func (sched *VDCScheduler) CheckForCrashedNodes(offers []*mesos.Offer, ctx conte return nil } +func (sched *VDCScheduler) storeOffers(offers []*mesos.Offer) { + for _, offer := range offers { + vdcOffer := convertToOpenVDCOffer(offer) + schedule.StoreOffer(vdcOffer) + } +} + func getDisconnectedInstances(offers []*mesos.Offer, ctx context.Context, driver sched.SchedulerDriver) []*model.Instance { disconnectedInstances := []*model.Instance{} - for _, offer := range offers { agentID := getAgentID(offer) diff --git a/scheduler/mesos_test.go b/scheduler/mesos_test.go new file mode 100644 index 00000000..a2676bee --- /dev/null +++ b/scheduler/mesos_test.go @@ -0,0 +1,154 @@ +package scheduler + +import ( + "testing" + + "github.com/axsh/openvdc/model" + "github.com/gogo/protobuf/proto" + mesos "github.com/mesos/mesos-go/mesosproto" + "github.com/stretchr/testify/assert" +) + +var ( + mesosOffer *mesos.Offer +) + +func init() { + mesosOffer = &mesos.Offer{ + Id: &mesos.OfferID{ + Value: proto.String("d39c0128-4822-49a0-9fab-640fba518d53-O590"), + XXX_unrecognized: []byte{}, + }, + FrameworkId: &mesos.FrameworkID{ + Value: proto.String("d39c0128-4822-49a0-9fab-640fba518d53-0004"), + XXX_unrecognized: []byte{}, + }, + SlaveId: &mesos.SlaveID{ + Value: proto.String("d39c0128-4822-49a0-9fab-640fba518d53-S0"), + XXX_unrecognized: []byte{}, + }, + Hostname: proto.String("10.141.141.10"), + Resources: []*mesos.Resource{ + &mesos.Resource{ + Name: proto.String("cpus"), + Type: mesos.Value_Type.Enum(mesos.Value_SCALAR), + Scalar: &mesos.Value_Scalar{ + Value: proto.Float64(2), + XXX_unrecognized: []byte{}, + }, + Ranges: nil, + Set: nil, + Role: proto.String("*"), + Disk: nil, + Reservation: nil, + Revocable: nil, + XXX_unrecognized: []byte{}, + }, + &mesos.Resource{ + Name: proto.String("mem"), + Type: mesos.Value_Type.Enum(mesos.Value_SCALAR), + Scalar: &mesos.Value_Scalar{ + Value: proto.Float64(1000.0), + XXX_unrecognized: []byte{}, + }, + Ranges: nil, + Set: nil, + Role: proto.String("*"), + Disk: nil, + Reservation: nil, + Revocable: nil, + XXX_unrecognized: []byte{}, + }, + &mesos.Resource{ + Name: proto.String("disk"), + Type: mesos.Value_Type.Enum(mesos.Value_SCALAR), + Scalar: &mesos.Value_Scalar{ + Value: proto.Float64(34068), + }, + // XXX_unrecognized: []byte{}, + Ranges: nil, + Set: nil, + Role: proto.String("*"), + Disk: nil, + Reservation: nil, + Revocable: nil, + XXX_unrecognized: []byte{}, + }, + &mesos.Resource{ + Name: proto.String("ports"), + Type: mesos.Value_Type.Enum(mesos.Value_RANGES), + Scalar: nil, + Ranges: &mesos.Value_Ranges{ + Range: []*mesos.Value_Range{ + &mesos.Value_Range{ + Begin: proto.Uint64(31000), + End: proto.Uint64(32000), + XXX_unrecognized: []byte{}, + }, + }, + XXX_unrecognized: []byte{}, + }, + Set: nil, + Role: proto.String("*"), + Disk: nil, + Reservation: nil, + Revocable: nil, + XXX_unrecognized: []byte{}, + }, + }, + ExecutorIds: []*mesos.ExecutorID{}, + Attributes: []*mesos.Attribute{}, + Url: &mesos.URL{ + Scheme: proto.String("http"), + Address: &mesos.Address{ + Hostname: proto.String("10.141.141.10"), + Ip: proto.String("127.0.1.1"), + Port: proto.Int32(5051), + XXX_unrecognized: []byte{}, + }, + Path: proto.String("/slave(1)"), + Query: []*mesos.Parameter{}, + Fragment: nil, + XXX_unrecognized: []byte{}, + }, + Unavailability: nil, + XXX_unrecognized: []byte{}, + } +} + +func TestConvertToOpenVDCOffer(t *testing.T) { + assert := assert.New(t) + + vOffer := &model.VDCOffer{ + SlaveID: "d39c0128-4822-49a0-9fab-640fba518d53-S0", + Resources: []model.VDCOfferResource{ + model.VDCOfferResource{ + Name: "cpus", + Type: model.VDCOfferValueScalar, + Scalar: 2.0, + }, + model.VDCOfferResource{ + Name: "mem", + Type: model.VDCOfferValueScalar, + Scalar: 1000.0, + }, + model.VDCOfferResource{ + Name: "disk", + Type: model.VDCOfferValueScalar, + Scalar: 34068.0, + }, + model.VDCOfferResource{ + Name: "ports", + Type: model.VDCOfferValueRanges, + Ranges: []model.VDCOfferValueRange{ + model.VDCOfferValueRange{ + Begin: 31000, + End: 32000, + }, + }, + }, + }, + } + m2vOffer := convertToOpenVDCOffer(mesosOffer) + assert.Equal(*m2vOffer, *vOffer) +} diff --git a/scheduler/schedule.go b/scheduler/schedule.go new file mode 100644 index 00000000..2120247e --- /dev/null +++ b/scheduler/schedule.go @@ -0,0 +1,95 @@ +package scheduler + +import ( + "fmt" + "sync" + "time" + + log "github.com/Sirupsen/logrus" + "github.com/axsh/openvdc/handlers" + "github.com/axsh/openvdc/model" +) + +var ( + // Time to wait for the offer to arrive + waitOfferSec = 5 + + instanceSchedulerHandlers = make(map[string]handlers.InstanceScheduleHandler) + schedule *Schedule +) + +func init() { + schedule = newSchedule() +} + +func RegisterInstanceScheduleHandler(name string, i handlers.InstanceScheduleHandler) error { + if _, exists := instanceSchedulerHandlers[name]; exists { + return fmt.Errorf("Duplicated name for instance schedule handler: %s", name) + } + instanceSchedulerHandlers[name] = i + return nil +} + +type Schedule struct { + mutex sync.RWMutex + storedOffers map[string]*model.VDCOffer +} + +func newSchedule() *Schedule { + return &Schedule{ + mutex: sync.RWMutex{}, + storedOffers: make(map[string]*model.VDCOffer), + } +} + +// TODO write definition of openvdc offer (interface | struct) +func (s *Schedule) StoreOffer(offer *model.VDCOffer) { + s.mutex.Lock() + defer s.mutex.Unlock() + schedule.storedOffers[offer.SlaveID] = offer +} + +func (s *Schedule) Assign(inst *model.Instance) error { + flog := log.WithFields(log.Fields{ + "instance_id": inst.GetId(), + }) + + name := inst.ResourceTemplate().ResourceName() + var instSchedHandler handlers.InstanceScheduleHandler + var ok bool + if instSchedHandler, ok = instanceSchedulerHandlers[name]; !ok { + return fmt.Errorf("%s instanceSchedulerHandlers is not registered", name) + } + var instResource model.InstanceResource + instResource, ok = inst.ResourceTemplate().(model.InstanceResource) + if !ok { + return fmt.Errorf("Templete do not have vcpu and mem", instResource) + } + + // wait for the offer from slave until waitOfferSec + // TODO test this function + for i := 0; i < waitOfferSec+1; i++ { + if i == waitOfferSec { + return fmt.Errorf("Exceeded the waiting time for the offer from the slave") + } + if len(schedule.storedOffers) == 0 { + time.Sleep(1) + } else { + break + } + } + + s.mutex.RLock() + defer s.mutex.RUnlock() + for _, offer := range schedule.storedOffers { + ok, err := instSchedHandler.ScheduleInstance(instResource, offer) + if err != nil { + return err + } + if ok { + flog.Infof("Assined") + return nil + } + } + return fmt.Errorf("There is no machine can satisfy resource requirement") +} diff --git a/templates/centos/7/lxc_huge.json b/templates/centos/7/lxc_huge.json new file mode 100644 index 00000000..fc7a4081 --- /dev/null +++ b/templates/centos/7/lxc_huge.json @@ -0,0 +1,14 @@ +{ + "title": "CentOS7", + "template": { + "type": "vm/lxc", + "vcpu": 9999, + "memory_gb": 9999, + "lxc_template": { + "openvdc": { + "distro": "centos", + "release": "7" + } + } + } +} diff --git a/vendor/vendor.json b/vendor/vendor.json index eb88b0b7..15240010 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -532,6 +532,12 @@ "revision": "adf4530b240711895896b14c337f9b77a3143e96", "revisionTime": "2017-11-27T00:49:25Z" }, + { + "checksumSHA1": "CDCNKlvOlAaguPUu8gbe7T4awZo=", + "path": "github.com/vmware/govmomi/govc/device/floppy", + "revision": "17b8c9ccb7f8c7b015d44c4ea39305c970a7bf31", + "revisionTime": "2017-09-05T23:36:42Z" + }, { "checksumSHA1": "jr2UFXDaZ8Lc3Yx5bEwJ+lSH7AA=", "path": "github.com/vmware/govmomi/govc/device/serial",